blob: 704eb95acb024688ef87d4f1ebf20b4f63a88aff [file] [log] [blame] [edit]
/* Copyright 2016 The Bazel Authors. All rights reserved.
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
http://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 merger_test
import (
"path/filepath"
"testing"
"github.com/bazelbuild/bazel-gazelle/language"
golang "github.com/bazelbuild/bazel-gazelle/language/go"
"github.com/bazelbuild/bazel-gazelle/language/proto"
"github.com/bazelbuild/bazel-gazelle/merger"
"github.com/bazelbuild/bazel-gazelle/rule"
)
// should fix
// * updated srcs from new
// * data and size preserved from old
// * load stmt fixed to those in use and sorted
type testCase struct {
desc, previous, current, empty, expected string
}
var testCases = []testCase{
{
desc: "basic functionality",
previous: `
load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library", "go_prefix", "go_test")
go_prefix("github.com/jr_hacker/tools")
go_library(
name = "go_default_library",
srcs = [
"lex.go",
"print.go",
"debug.go",
],
)
go_test(
name = "go_default_test",
size = "small",
srcs = [
"gen_test.go", # keep
"parse_test.go",
],
data = glob(["testdata/*"]),
embed = [":go_default_library"],
)
`,
current: `
load("@io_bazel_rules_go//go:def.bzl", "go_test", "go_library")
go_prefix("")
go_library(
name = "go_default_library",
srcs = [
"lex.go",
"print.go",
],
)
go_test(
name = "go_default_test",
srcs = [
"parse_test.go",
"print_test.go",
],
embed = [":go_default_library"],
)
`,
expected: `
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_prefix", "go_test")
go_prefix("github.com/jr_hacker/tools")
go_library(
name = "go_default_library",
srcs = [
"lex.go",
"print.go",
],
)
go_test(
name = "go_default_test",
size = "small",
srcs = [
"gen_test.go", # keep
"parse_test.go",
"print_test.go",
],
data = glob(["testdata/*"]),
embed = [":go_default_library"],
)
`,
}, {
desc: "merge dicts",
previous: `
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = select({
"darwin_amd64": [
"foo_darwin_amd64.go", # keep
"bar_darwin_amd64.go",
],
"linux_arm": [
"foo_linux_arm.go", # keep
"bar_linux_arm.go",
],
}),
)
`,
current: `
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = select({
"linux_arm": ["baz_linux_arm.go"],
"darwin_amd64": ["baz_darwin_amd64.go"],
"//conditions:default": [],
}),
)
`,
expected: `
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = select({
"darwin_amd64": [
"baz_darwin_amd64.go",
"foo_darwin_amd64.go", # keep
],
"linux_arm": [
"baz_linux_arm.go",
"foo_linux_arm.go", # keep
],
"//conditions:default": [],
}),
)
`,
}, {
desc: "merge old dict with gen list",
previous: `
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = select({
"linux_arm": [
"foo_linux_arm.go", # keep
"bar_linux_arm.go", # keep
],
"darwin_amd64": [
"bar_darwin_amd64.go",
],
}),
)
`,
current: `
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = ["baz.go"],
)
`,
expected: `
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = [
"baz.go",
] + select({
"linux_arm": [
"bar_linux_arm.go", # keep
"foo_linux_arm.go", # keep
],
}),
)
`,
}, {
desc: "merge old list with gen dict",
previous: `
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = [
"foo.go", # keep
"bar.go", # keep
],
)
`,
current: `
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = select({
"linux_arm": [
"foo_linux_arm.go",
"bar_linux_arm.go",
],
"darwin_amd64": [
"bar_darwin_amd64.go",
],
"//conditions:default": [],
}),
)
`,
expected: `
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = [
"bar.go", # keep
"foo.go", # keep
] + select({
"linux_arm": [
"bar_linux_arm.go",
"foo_linux_arm.go",
],
"darwin_amd64": [
"bar_darwin_amd64.go",
],
"//conditions:default": [],
}),
)
`,
}, {
desc: "merge old list and dict with gen list and dict",
previous: `
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = [
"foo.go", # keep
"bar.go",
] + select({
"linux_arm": [
"foo_linux_arm.go", # keep
],
"//conditions:default": [],
}),
)
`,
current: `
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = ["baz.go"] + select({
"linux_arm": ["bar_linux_arm.go"],
"darwin_amd64": ["foo_darwin_amd64.go"],
"//conditions:default": [],
}),
)
`,
expected: `
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = [
"baz.go",
"foo.go", # keep
] + select({
"darwin_amd64": ["foo_darwin_amd64.go"],
"linux_arm": [
"bar_linux_arm.go",
"foo_linux_arm.go", # keep
],
"//conditions:default": [],
}),
)
`,
}, {
desc: "os and arch",
previous: `
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = [
"generic_1.go",
] + select({
"@io_bazel_rules_go//go/platform:linux": [
"os_linux.go", # keep
],
"//conditions:default": [],
}),
)
`,
current: `
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = [
"generic_2.go",
] + select({
"@io_bazel_rules_go//go/platform:amd64": ["arch_amd64.go"],
"//conditions:default": [],
}),
)
`,
expected: `
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = [
"generic_2.go",
] + select({
"@io_bazel_rules_go//go/platform:linux": [
"os_linux.go", # keep
],
"//conditions:default": [],
}) + select({
"@io_bazel_rules_go//go/platform:amd64": ["arch_amd64.go"],
"//conditions:default": [],
}),
)
`,
}, {
desc: "merge error keeps old",
previous: `
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = glob(["*.go"]),
)
`,
current: `
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = ["foo.go"],
)
`,
expected: `
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = glob(["*.go"]),
)
`,
}, {
desc: "delete empty list",
previous: `
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = ["deleted.go"],
)
`,
current: `
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = select({
"linux_arm": ["foo_linux_arm.go"],
}),
)
`,
expected: `
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = select({
"linux_arm": ["foo_linux_arm.go"],
}),
)
`,
}, {
desc: "delete empty dict",
previous: `
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = select({
"linux_arm": ["foo_linux_arm.go"],
"//conditions:default": [],
}),
)
`,
current: `
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = ["foo.go"],
)
`,
expected: `
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = ["foo.go"],
)
`,
}, {
desc: "delete empty attr",
previous: `
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = ["foo.go"],
embed = ["deleted"],
)
`,
current: `
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = ["foo.go"],
)
`,
expected: `
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = ["foo.go"],
)
`,
}, {
desc: "delete empty lists",
previous: `
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = ["foo.go"],
embed = ["deleted"],
)
`,
current: `
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = ["foo.go"],
embed = [],
)
`,
expected: `
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = ["foo.go"],
)
`,
}, {
desc: "merge comments",
previous: `
# load
load("@io_bazel_rules_go//go:def.bzl", "go_library")
# rule
go_library(
# unmerged attr
name = "go_default_library",
# merged attr
srcs = ["foo.go"],
)
`,
current: `
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = ["foo.go"],
)
`,
expected: `
# load
load("@io_bazel_rules_go//go:def.bzl", "go_library")
# rule
go_library(
# unmerged attr
name = "go_default_library",
# merged attr
srcs = ["foo.go"],
)
`,
}, {
desc: "preserve comments",
previous: `
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = [
"a.go", # preserve
"b.go", # comments
],
)
`,
current: `
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = ["a.go", "b.go"],
)
`,
expected: `
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = [
"a.go", # preserve
"b.go", # comments
],
)
`,
}, {
desc: "merge copts and clinkopts",
previous: `
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "cgo_default_library",
copts = [
"-O0",
"-g", # keep
],
clinkopts = [
"-lX11",
],
)
`,
current: `
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "cgo_default_library",
cgo = True,
copts = [
"-O2",
],
clinkopts = [
"-lpng",
],
)
`,
expected: `
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "cgo_default_library",
cgo = True,
clinkopts = [
"-lpng",
],
copts = [
"-g", # keep
"-O2",
],
)
`,
}, {
desc: "keep scalar attr",
previous: `
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
embed = [":lib"], # keep
)
`,
current: `
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
)
`,
expected: `
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
embed = [":lib"], # keep
)
`,
}, {
desc: "don't delete list with keep",
previous: `
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = [
"one.go", # keep
],
)
`,
current: `
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
)
`,
expected: `
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = [
"one.go", # keep
],
)
`,
}, {
desc: "keep list multiline",
previous: `
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = [
"one.go", # keep
"two.go",
],
)
`,
current: `
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
)
`,
expected: `
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = [
"one.go", # keep
],
)
`,
}, {
desc: "keep dict list multiline",
previous: `
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = select({
"darwin_amd64": [
"one_darwin.go", # keep
],
"linux_arm": [
"one_linux.go", # keep
"two_linux.go",
],
}),
)
`,
current: `
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
)
`,
expected: `
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = select({
"darwin_amd64": [
"one_darwin.go", # keep
],
"linux_arm": [
"one_linux.go", # keep
],
}),
)
`,
}, {
desc: "keep prevents delete",
previous: `
load("@io_bazel_rules_go//go:def.bzl", "go_library")
# keep
go_library(
name = "go_default_library",
srcs = ["lib.go"],
)
`,
empty: `
go_library(name = "go_default_library")
`,
expected: `
load("@io_bazel_rules_go//go:def.bzl", "go_library")
# keep
go_library(
name = "go_default_library",
srcs = ["lib.go"],
)
`,
}, {
desc: "keep prevents merge",
previous: `
load("@io_bazel_rules_go//go:def.bzl", "go_library")
# keep
go_library(
name = "go_default_library",
srcs = ["old.go"],
)
`,
current: `
go_library(
name = "go_default_library",
srcs = ["new.go"],
)
`,
expected: `
load("@io_bazel_rules_go//go:def.bzl", "go_library")
# keep
go_library(
name = "go_default_library",
srcs = ["old.go"],
)
`,
}, {
desc: "delete empty rule",
previous: `
load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library")
go_library(
name = "go_default_library",
srcs = ["lib.go"],
)
go_binary(
name = "old",
srcs = ["bin.go"],
embed = [":go_default_library"],
importpath = "foo",
)
`,
current: `
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = ["lib.go"],
)
`,
empty: `
go_binary(name = "old")
`,
expected: `
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = ["lib.go"],
)
`,
}, {
desc: "don't delete kept rule",
previous: `
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = [
"lib.go", # keep
],
)
`,
empty: `go_library(name = "go_default_library")`,
expected: `
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = [
"lib.go", # keep
],
)
`,
}, {
desc: "match and rename",
previous: `
load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library")
load("@io_bazel_rules_go//proto:def.bzl", "go_proto_library")
go_binary(
name = "custom_bin",
embed = [":custom_library"],
)
go_library(
name = "custom_library",
embed = [":custom_proto_library"],
importpath = "example.com/repo/foo",
)
go_proto_library(
name = "custom_proto_library",
importpath = "example.com/repo/foo",
proto = ":foo_proto",
)
`,
current: `
go_binary(
name = "bin",
srcs = ["bin.go"],
embed = [":go_default_library"],
)
go_library(
name = "go_default_library",
srcs = ["lib.go"],
embed = [":foo_proto_library"],
importpath = "example.com/repo/foo",
)
go_proto_library(
name = "foo_proto_library",
importpath = "example.com/repo/foo",
proto = ":foo_proto",
)
`,
expected: `
load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library")
load("@io_bazel_rules_go//proto:def.bzl", "go_proto_library")
go_binary(
name = "custom_bin",
srcs = ["bin.go"],
embed = [":custom_library"],
)
go_library(
name = "custom_library",
srcs = ["lib.go"],
embed = [":custom_proto_library"],
importpath = "example.com/repo/foo",
)
go_proto_library(
name = "custom_proto_library",
importpath = "example.com/repo/foo",
proto = ":foo_proto",
)
`,
}, {
desc: "struct macro",
previous: `load("@bazel_skylib//lib:selects.bzl", "selects")
selects.config_setting_group(
name = "a",
match_any = [
"//:config_a",
"//:config_b",
],
)
`,
current: `load("@bazel_skylib//lib:selects.bzl", "selects")
selects.config_setting_group(
name = "a",
match_any = [
"//:config_c",
"//:config_d",
],
)
`,
expected: `load("@bazel_skylib//lib:selects.bzl", "selects")
selects.config_setting_group(
name = "a",
match_any = [
"//:config_c",
"//:config_d",
],
)
`,
},
}
func TestMergeFile(t *testing.T) {
for _, tc := range testCases {
t.Run(tc.desc, func(t *testing.T) {
genFile, err := rule.LoadData(filepath.Join("current", "BUILD.bazel"), "", []byte(tc.current))
if err != nil {
t.Fatalf("%s: %v", tc.desc, err)
}
f, err := rule.LoadData(filepath.Join("previous", "BUILD.bazel"), "", []byte(tc.previous))
if err != nil {
t.Fatalf("%s: %v", tc.desc, err)
}
emptyFile, err := rule.LoadData(filepath.Join("empty", "BUILD.bazel"), "", []byte(tc.empty))
if err != nil {
t.Fatalf("%s: %v", tc.desc, err)
}
merger.MergeFile(f, emptyFile.Rules, genFile.Rules, merger.PreResolve, testKinds)
merger.FixLoads(f, testLoads)
want := tc.expected
if len(want) > 0 && want[0] == '\n' {
want = want[1:]
}
if got := string(f.Format()); got != want {
t.Fatalf("%s: got %s; want %s", tc.desc, got, want)
}
})
}
}
var (
testKinds map[string]rule.KindInfo
testLoads []rule.LoadInfo
)
func init() {
testKinds = make(map[string]rule.KindInfo)
langs := []language.Language{proto.NewLanguage(), golang.NewLanguage()}
for _, lang := range langs {
for kind, info := range lang.Kinds() {
testKinds[kind] = info
}
loads := lang.(language.ModuleAwareLanguage).ApparentLoads(func(s string) string {
return ""
})
testLoads = append(testLoads, loads...)
}
// add skylib defs
testLoads = append(testLoads, rule.LoadInfo{
Name: "@bazel_skylib//lib:selects.bzl",
Symbols: []string{
"selects",
},
})
testKinds["selects.config_setting_group"] = rule.KindInfo{
MergeableAttrs: map[string]bool{
"match_all": true,
"match_any": true,
},
}
}
func TestMatch(t *testing.T) {
for _, tc := range []struct {
desc, gen, old string
wantIndex int
wantError bool
}{
{
desc: "no_match",
gen: `go_library(name = "lib")`,
wantIndex: -1,
}, {
desc: "name_match",
gen: `go_library(name = "lib")`,
old: `go_library(name = "lib", srcs = ["lib.go"])`,
wantIndex: 0,
}, {
desc: "name_match_kind_different",
gen: `go_library(name = "lib")`,
old: `cc_library(name = "lib")`,
wantError: true,
}, {
desc: "attr_match",
gen: `go_library(name = "x", importpath = "foo")`,
old: `go_library(name = "y", importpath = "foo")`,
wantIndex: 0,
}, {
desc: "multiple_attr_match",
gen: `go_library(name = "x", importpath = "foo")`,
old: `
go_library(name = "y", importpath = "foo")
go_library(name = "z", importpath = "foo")
`,
wantError: true,
}, {
desc: "any_match",
gen: `go_binary(name = "x")`,
old: `go_binary(name = "y")`,
wantIndex: 0,
}, {
desc: "multiple_any_match",
gen: `go_binary(name = "x")`,
old: `
go_binary(name = "y")
go_binary(name = "z")
`,
wantError: true,
}, {
desc: "srcs match",
gen: `proto_library(name = "proto1", srcs = ["foo.proto", "bar.proto"])`,
old: `proto_library(name = "proto2", srcs = ["bar.proto", "foo.proto"])`,
wantIndex: 0,
}, {
desc: "importpath match",
gen: `go_proto_library(name = "go_proto1", importpath="example.com/foo")`,
old: `go_proto_library(name = "go_proto2", importpath="example.com/foo")`,
}, {
desc: "struct macro match",
gen: `selects.config_setting_group(name = "conf_group_1", match_any = ["//:config_a", "//:config_b"])`,
old: `selects.config_setting_group(name = "conf_group_1", match_any = ["//:config_c", "//:config_d"])`,
wantIndex: 0,
},
} {
t.Run(tc.desc, func(t *testing.T) {
genFile, err := rule.LoadData(filepath.Join("gen", "BUILD.bazel"), "", []byte(tc.gen))
if err != nil {
t.Fatal(err)
}
oldFile, err := rule.LoadData(filepath.Join("old", "BUILD.bazel"), "", []byte(tc.old))
if err != nil {
t.Fatal(err)
}
r := genFile.Rules[0]
info := testKinds[r.Kind()]
if got, gotErr := merger.Match(oldFile.Rules, r, info); gotErr != nil {
if !tc.wantError {
t.Errorf("unexpected error: %v", gotErr)
}
} else if tc.wantError {
t.Error("unexpected success")
} else if got == nil && tc.wantIndex >= 0 {
t.Errorf("got nil; want index %d", tc.wantIndex)
} else if got != nil && got.Index() != tc.wantIndex {
t.Fatalf("got index %d ; want %d", got.Index(), tc.wantIndex)
}
})
}
}