| /* | |
| Copyright 2023 Google LLC | |
| Licensed under the Apache License, Version 2.0 (the "License"); | |
| you may not use this file except in compliance with the License. | |
| You may obtain a copy of the License at | |
| https://www.apache.org/licenses/LICENSE-2.0 | |
| Unless required by applicable law or agreed to in writing, software | |
| distributed under the License is distributed on an "AS IS" BASIS, | |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| See the License for the specific language governing permissions and | |
| limitations under the License. | |
| */ | |
| package bzlmod | |
| import ( | |
| "reflect" | |
| "strconv" | |
| "testing" | |
| "github.com/bazelbuild/buildtools/build" | |
| ) | |
| const proxiesNoModuleHeader = `` | |
| const proxiesModuleNameHeader = ` | |
| module(name = "name") | |
| ` | |
| const proxiesModuleRepoNameHeader = ` | |
| module(name = "name", repo_name = "repo_name") | |
| ` | |
| const proxiesBody = ` | |
| prox1 = use_extension("@name//bzl:extensions.bzl", "ext") | |
| prox1.use(name = "foo") | |
| isolated_prox1 = use_extension("@name//bzl:extensions.bzl", "ext", dev_dependency = False, isolate = True) | |
| isolated_prox1.use(name = "foo") | |
| prox2 = use_extension("@name//bzl:extensions.bzl", "ext", dev_dependency = True) | |
| prox2.use(list = ["foo", "bar"]) | |
| # some comment | |
| prox3 = use_extension("@repo_name//bzl:extensions.bzl", "ext") | |
| prox3.use(label = "@dep//:bar") | |
| isolated_prox2 = use_extension("@name//bzl:extensions.bzl", "ext", dev_dependency = True, isolate = True) | |
| isolated_prox2.use(name = "foo") | |
| prox4 = use_extension("@repo_name//bzl:extensions.bzl", "ext", dev_dependency = True) | |
| prox4.use(dict = {"foo": "bar"}) | |
| isolated_prox3 = use_extension("@name//bzl:extensions.bzl", "ext", dev_dependency = True, isolate = True) | |
| isolated_prox3.use(name = "foo") | |
| prox5 = use_extension("@//bzl:extensions.bzl", "ext") | |
| prox5.use(name = "foo") | |
| prox6 = use_extension("@//bzl:extensions.bzl", "ext", dev_dependency = True) | |
| prox6.use(list = ["foo", "bar"]) | |
| prox7 = use_extension("//bzl:extensions.bzl", "ext", dev_dependency = False) | |
| prox7.use(label = "@foo//:bar") | |
| isolated_prox4 = use_extension("@name//bzl:extensions.bzl", "ext", isolate = True) | |
| isolated_prox4.use(name = "foo") | |
| prox8 = use_extension("//bzl:extensions.bzl", "ext", dev_dependency = True) | |
| unused = prox8.use(dict = {"foo": "bar"}) | |
| prox9 = use_extension( | |
| # comment | |
| "@dep//:extensions.bzl", "other_ext") | |
| prox9.use(label = "@name//:bar") | |
| prox10 = use_extension("@dep//:extensions.bzl", "other_ext", dev_dependency = bool(1)) | |
| prox10.use(dict = {"foo": "bar"}) | |
| prox11 = use_extension("extension.bzl", "ext") | |
| prox12 = use_extension(":extension.bzl", "ext") | |
| prox13 = use_extension("//:extension.bzl", "ext") | |
| prox14 = use_extension("@name//:extension.bzl", "ext") | |
| prox15 = use_extension("@repo_name//:extension.bzl", "ext") | |
| ` | |
| func TestProxies(t *testing.T) { | |
| for i, tc := range []struct { | |
| content string | |
| extBzlFiles []string | |
| extName string | |
| dev bool | |
| expectedProxies []string | |
| }{ | |
| { | |
| proxiesNoModuleHeader + proxiesBody, | |
| []string{"//bzl:extensions.bzl", "@//bzl:extensions.bzl"}, | |
| "ext", | |
| false, | |
| []string{"prox5", "prox7"}, | |
| }, | |
| { | |
| proxiesNoModuleHeader + proxiesBody, | |
| []string{"//bzl:extensions.bzl", "@//bzl:extensions.bzl"}, | |
| "ext", | |
| true, | |
| []string{"prox6", "prox8"}, | |
| }, | |
| { | |
| proxiesModuleNameHeader + proxiesBody, | |
| []string{"//bzl:extensions.bzl", "@//bzl:extensions.bzl", "@name//bzl:extensions.bzl"}, | |
| "ext", | |
| false, | |
| []string{"prox1", "prox5", "prox7"}, | |
| }, | |
| { | |
| proxiesModuleNameHeader + proxiesBody, | |
| []string{"//bzl:extensions.bzl", "@//bzl:extensions.bzl", "@name//bzl:extensions.bzl"}, | |
| "ext", | |
| true, | |
| []string{"prox2", "prox6", "prox8"}, | |
| }, | |
| { | |
| proxiesModuleRepoNameHeader + proxiesBody, | |
| []string{"//bzl:extensions.bzl", "@//bzl:extensions.bzl", "@repo_name//bzl:extensions.bzl"}, | |
| "ext", | |
| false, | |
| []string{"prox3", "prox5", "prox7"}, | |
| }, | |
| { | |
| proxiesModuleRepoNameHeader + proxiesBody, | |
| []string{"//bzl:extensions.bzl", "@//bzl:extensions.bzl", "@repo_name//bzl:extensions.bzl"}, | |
| "ext", | |
| true, | |
| []string{"prox4", "prox6", "prox8"}, | |
| }, | |
| { | |
| proxiesModuleRepoNameHeader + proxiesBody, | |
| []string{"@name//bzl:extensions.bzl"}, | |
| "ext", | |
| false, | |
| []string{"prox1"}, | |
| }, | |
| { | |
| proxiesModuleRepoNameHeader + proxiesBody, | |
| []string{"@dep//:extensions.bzl"}, | |
| "other_ext", | |
| false, | |
| []string{"prox9"}, | |
| }, | |
| { | |
| proxiesModuleRepoNameHeader + proxiesBody, | |
| []string{"@dep//:extensions.bzl"}, | |
| "other_ext", | |
| true, | |
| []string{"prox10"}, | |
| }, | |
| { | |
| proxiesModuleNameHeader + proxiesBody, | |
| []string{"//:extension.bzl", "@//:extension.bzl"}, | |
| "ext", | |
| false, | |
| []string{"prox11", "prox12", "prox13", "prox14"}, | |
| }, | |
| { | |
| proxiesModuleRepoNameHeader + proxiesBody, | |
| []string{"//:extension.bzl", "@//:extension.bzl"}, | |
| "ext", | |
| false, | |
| []string{"prox11", "prox12", "prox13", "prox15"}, | |
| }, | |
| } { | |
| t.Run(strconv.Itoa(i), func(t *testing.T) { | |
| for _, extBzlFile := range tc.extBzlFiles { | |
| t.Run("label_"+extBzlFile, func(t *testing.T) { | |
| f, err := build.ParseModule("MODULE.bazel", []byte(tc.content)) | |
| if err != nil { | |
| t.Fatal(err) | |
| } | |
| actualProxies := Proxies(f, extBzlFile, tc.extName, tc.dev) | |
| if !reflect.DeepEqual(actualProxies, tc.expectedProxies) { | |
| t.Error("want: ", tc.expectedProxies, ", got: ", actualProxies) | |
| } | |
| }) | |
| } | |
| }) | |
| } | |
| } | |
| func TestAllProxies(t *testing.T) { | |
| for i, tc := range []struct { | |
| proxy string | |
| expectedProxies []string | |
| }{ | |
| { | |
| "invalid_proxy", | |
| nil, | |
| }, | |
| { | |
| "isolated_prox1", | |
| []string{"isolated_prox1"}, | |
| }, | |
| { | |
| "isolated_prox2", | |
| []string{"isolated_prox2"}, | |
| }, | |
| { | |
| "isolated_prox3", | |
| []string{"isolated_prox3"}, | |
| }, | |
| { | |
| "isolated_prox4", | |
| []string{"isolated_prox4"}, | |
| }, | |
| { | |
| "prox1", | |
| []string{"prox1", "prox5", "prox7"}, | |
| }, | |
| { | |
| "prox2", | |
| []string{"prox2", "prox6", "prox8"}, | |
| }, | |
| { | |
| "prox9", | |
| []string{"prox9"}, | |
| }, | |
| { | |
| "prox10", | |
| []string{"prox10"}, | |
| }, | |
| } { | |
| t.Run(strconv.Itoa(i), func(t *testing.T) { | |
| f, err := build.ParseModule("MODULE.bazel", []byte(proxiesModuleNameHeader+proxiesBody)) | |
| if err != nil { | |
| t.Fatal(err) | |
| } | |
| proxies := AllProxies(f, tc.proxy) | |
| if !reflect.DeepEqual(proxies, tc.expectedProxies) { | |
| t.Error("want: ", tc.expectedProxies, ", got: ", proxies) | |
| } | |
| }) | |
| } | |
| } | |
| const useReposFile = ` | |
| prox1 = use_extension("@mod//bzl:extensions.bzl", "ext") | |
| prox1.use() | |
| prox1.config() | |
| prox2 = use_extension("//bzl:extensions.bzl", "ext") | |
| use_repo(prox2) | |
| use_repo(prox1, "repo5") | |
| prox3 = use_extension("@dep//bzl:extensions.bzl", "ext") | |
| prox2_dev = use_extension("//bzl:extensions.bzl", "ext", dev_dependency = True) | |
| use_repo(prox1, "repo1") | |
| use_repo(prox3, "repo2") | |
| use_repo(prox2, "repo3", "repo4") | |
| ` | |
| func TestUseRepos(t *testing.T) { | |
| for i, tc := range []struct { | |
| content string | |
| proxies []string | |
| expectedStmts []int | |
| }{ | |
| { | |
| useReposFile, | |
| []string{"prox2"}, | |
| []int{4, 10}, | |
| }, | |
| { | |
| useReposFile, | |
| []string{"prox1", "prox3"}, | |
| []int{5, 8, 9}, | |
| }, | |
| { | |
| useReposFile, | |
| []string{"prox2_dev"}, | |
| []int{}, | |
| }, | |
| } { | |
| t.Run(strconv.Itoa(i), func(t *testing.T) { | |
| f, err := build.ParseModule("MODULE.bazel", []byte(tc.content)) | |
| if err != nil { | |
| t.Fatal(err) | |
| } | |
| var expectedUseRepos []*build.CallExpr | |
| for _, stmt := range tc.expectedStmts { | |
| expectedUseRepos = append(expectedUseRepos, f.Stmt[stmt].(*build.CallExpr)) | |
| } | |
| actualUseRepos := UseRepos(f, tc.proxies) | |
| if !reflect.DeepEqual(actualUseRepos, expectedUseRepos) { | |
| t.Error("want: ", expectedUseRepos, ", got: ", actualUseRepos) | |
| } | |
| }) | |
| } | |
| } | |
| func TestNewUseRepos(t *testing.T) { | |
| for i, tc := range []struct { | |
| content string | |
| proxies []string | |
| expectedContent string | |
| expectedUseRepo int | |
| }{ | |
| { | |
| ``, | |
| []string{"prox1"}, | |
| ``, | |
| -1, | |
| }, | |
| { | |
| `prox1 = use_extension("@module//:lib.bzl", "ext")`, | |
| []string{"prox2"}, | |
| `prox1 = use_extension("@module//:lib.bzl", "ext") | |
| `, | |
| -1, | |
| }, | |
| { | |
| `prox1 = use_extension("@mod//bzl:extensions.bzl", "ext", dev_dependency = True)`, | |
| []string{"prox1"}, | |
| `prox1 = use_extension("@mod//bzl:extensions.bzl", "ext", dev_dependency = True) | |
| use_repo(prox1) | |
| `, | |
| 1, | |
| }, | |
| { | |
| `prox1 = use_extension("@mod//bzl:extensions.bzl", "ext") | |
| prox1.config() | |
| prox1.download(name = "foo")`, | |
| []string{"prox1"}, | |
| `prox1 = use_extension("@mod//bzl:extensions.bzl", "ext") | |
| prox1.config() | |
| prox1.download(name = "foo") | |
| use_repo(prox1) | |
| `, | |
| 3, | |
| }, | |
| { | |
| `go_deps = use_extension("@gazelle//:extensions.bzl", "go_deps") | |
| go_deps.from_file(go_mod = "//:go.mod") | |
| pull = use_extension("@rules_oci//oci:pull.bzl", "go_deps") | |
| pull.oci_pull(name = "distroless_base") | |
| `, | |
| []string{"go_deps"}, | |
| `go_deps = use_extension("@gazelle//:extensions.bzl", "go_deps") | |
| go_deps.from_file(go_mod = "//:go.mod") | |
| use_repo(go_deps) | |
| pull = use_extension("@rules_oci//oci:pull.bzl", "go_deps") | |
| pull.oci_pull(name = "distroless_base") | |
| `, | |
| 2, | |
| }, | |
| { | |
| `go_deps = use_extension("@gazelle//:extensions.bzl", "go_deps") | |
| go_deps.from_file(go_mod = "//:go.mod") | |
| pull = use_extension("@rules_oci//oci:pull.bzl", "go_deps") | |
| `, | |
| []string{"go_deps"}, | |
| `go_deps = use_extension("@gazelle//:extensions.bzl", "go_deps") | |
| go_deps.from_file(go_mod = "//:go.mod") | |
| use_repo(go_deps) | |
| pull = use_extension("@rules_oci//oci:pull.bzl", "go_deps") | |
| `, | |
| 2, | |
| }, | |
| } { | |
| t.Run(strconv.Itoa(i), func(t *testing.T) { | |
| f, err := build.ParseModule("MODULE.bazel", []byte(tc.content)) | |
| if err != nil { | |
| t.Fatal(err) | |
| } | |
| f, actualNewUseRepo := NewUseRepo(f, tc.proxies) | |
| actualContent := string(build.Format(f)) | |
| if actualNewUseRepo != nil { | |
| if !reflect.DeepEqual(actualNewUseRepo, f.Stmt[tc.expectedUseRepo]) { | |
| t.Error("want: ", f.Stmt[tc.expectedUseRepo], ", got: ", actualNewUseRepo) | |
| } | |
| } else { | |
| if tc.expectedUseRepo != -1 { | |
| t.Error("wanted a nil new use_repo") | |
| } | |
| } | |
| if !reflect.DeepEqual(actualContent, tc.expectedContent) { | |
| t.Errorf("want:\n%q\ngot:\n%q\n", tc.expectedContent, actualContent) | |
| } | |
| }) | |
| } | |
| } | |
| func TestAddRepoUsages(t *testing.T) { | |
| for i, tc := range []struct { | |
| content string | |
| repos []string | |
| expectedContent string | |
| }{ | |
| { | |
| ``, | |
| []string{}, | |
| ``, | |
| }, | |
| { | |
| `use_repo(prox)`, | |
| []string{"repo2", "repo1"}, | |
| `use_repo(prox, "repo1", "repo2") | |
| `, | |
| }, | |
| { | |
| `use_repo( | |
| prox, | |
| )`, | |
| []string{"repo2", "repo1"}, | |
| `use_repo( | |
| prox, | |
| "repo1", | |
| "repo2", | |
| ) | |
| `, | |
| }, | |
| { | |
| `use_repo(prox, "repo2")`, | |
| []string{"repo2", "repo1"}, | |
| `use_repo(prox, "repo1", "repo2") | |
| `, | |
| }, | |
| { | |
| `use_repo( | |
| prox, | |
| "repo2", | |
| )`, | |
| []string{"repo2", "repo1"}, | |
| `use_repo( | |
| prox, | |
| "repo1", | |
| "repo2", | |
| ) | |
| `, | |
| }, | |
| { | |
| `use_repo(prox, "repo1") | |
| use_repo( | |
| prox2, | |
| my_repo = "repo2", | |
| "repo5", | |
| ) | |
| use_repo(prox, "repo3")`, | |
| []string{"repo3", "repo2", "repo4"}, | |
| `use_repo(prox, "repo1") | |
| use_repo( | |
| prox2, | |
| "repo5", | |
| my_repo = "repo2", | |
| ) | |
| use_repo(prox, "repo3", "repo4") | |
| `, | |
| }, | |
| } { | |
| t.Run(strconv.Itoa(i), func(t *testing.T) { | |
| f, err := build.ParseModule("MODULE.bazel", []byte(tc.content)) | |
| if err != nil { | |
| t.Fatal(err) | |
| } | |
| var useRepos []*build.CallExpr | |
| for _, stmt := range f.Stmt { | |
| useRepos = append(useRepos, stmt.(*build.CallExpr)) | |
| } | |
| AddRepoUsages(useRepos, tc.repos...) | |
| actualContent := string(build.Format(f)) | |
| if !reflect.DeepEqual(actualContent, tc.expectedContent) { | |
| t.Errorf("want:\n%q\ngot:\n%q\n", tc.expectedContent, actualContent) | |
| } | |
| }) | |
| } | |
| } | |
| func TestRemoveRepoUsages(t *testing.T) { | |
| for i, tc := range []struct { | |
| content string | |
| repos []string | |
| expectedContent string | |
| }{ | |
| { | |
| ``, | |
| []string{"repo1"}, | |
| ``, | |
| }, | |
| { | |
| `use_repo(prox)`, | |
| []string{"repo2", "repo1"}, | |
| `use_repo(prox) | |
| `, | |
| }, | |
| { | |
| `use_repo( | |
| prox, | |
| )`, | |
| []string{"repo2", "repo1"}, | |
| `use_repo( | |
| prox, | |
| ) | |
| `, | |
| }, | |
| { | |
| `use_repo(prox, "repo2")`, | |
| []string{"repo2", "repo1"}, | |
| `use_repo(prox) | |
| `, | |
| }, | |
| { | |
| `use_repo( | |
| prox, | |
| "repo2", | |
| )`, | |
| []string{"repo2", "repo1"}, | |
| `use_repo(prox) | |
| `, | |
| }, | |
| { | |
| `use_repo(prox, "repo1") | |
| use_repo( | |
| prox2, | |
| my_repo = "repo2", | |
| "repo5", | |
| ) | |
| use_repo(prox, "repo3")`, | |
| []string{"repo3", "repo2", "repo4"}, | |
| `use_repo(prox, "repo1") | |
| use_repo( | |
| prox2, | |
| "repo5", | |
| ) | |
| use_repo(prox) | |
| `, | |
| }, | |
| } { | |
| t.Run(strconv.Itoa(i), func(t *testing.T) { | |
| f, err := build.ParseModule("MODULE.bazel", []byte(tc.content)) | |
| if err != nil { | |
| t.Fatal(err) | |
| } | |
| var useRepos []*build.CallExpr | |
| for _, stmt := range f.Stmt { | |
| useRepos = append(useRepos, stmt.(*build.CallExpr)) | |
| } | |
| RemoveRepoUsages(useRepos, tc.repos...) | |
| actualContent := string(build.Format(f)) | |
| if !reflect.DeepEqual(actualContent, tc.expectedContent) { | |
| t.Errorf("want:\n%q\ngot:\n%q\n", tc.expectedContent, actualContent) | |
| } | |
| }) | |
| } | |
| } |