Skip to content

Commit

Permalink
Merge #79299
Browse files Browse the repository at this point in the history
79299: testutils/buildutil: add disallowed_imports_test bazel macro r=rickystewart a=ajwerner

This macro can be used to create a test which disallows one go package from
importing another. It uses a thin aspect to accumulate go dependencies while
avoiding any cycle due to nogo.

There's an example of its use in `execinfrapb`.

Relates heavily to #74176.

Release note: None

Co-authored-by: Andrew Werner <[email protected]>
  • Loading branch information
craig[bot] and ajwerner committed Apr 4, 2022
2 parents 1ede844 + cd890f5 commit 11d4026
Show file tree
Hide file tree
Showing 4 changed files with 78 additions and 3 deletions.
1 change: 1 addition & 0 deletions pkg/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,7 @@ ALL_TESTS = [
"//pkg/sql/doctor:doctor_test",
"//pkg/sql/enum:enum_test",
"//pkg/sql/execinfra:execinfra_test",
"//pkg/sql/execinfrapb:execinfrapb_disallowed_imports_test",
"//pkg/sql/execinfrapb:execinfrapb_test",
"//pkg/sql/execstats:execstats_test",
"//pkg/sql/flowinfra:flowinfra_test",
Expand Down
10 changes: 7 additions & 3 deletions pkg/cmd/generate-test-suites/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,15 +39,19 @@ func main() {
packagesToQuery = append(packagesToQuery, fmt.Sprintf("//pkg/%s/...", info.Name()))
}
allPackages := strings.Join(packagesToQuery, "+")
queryArgs := []string{"query", fmt.Sprintf("kind(go_test, %s)", allPackages), "--output=label"}
buf, err := exec.Command("bazel", queryArgs...).Output()
cmd := exec.Command(
"bazel", "query",
fmt.Sprintf(`kind("(go|sh)_test", %s)`, allPackages),
"--output=label",
)
buf, err := cmd.Output()
if err != nil {
log.Printf("Could not query Bazel tests: got error %v", err)
var cmderr *exec.ExitError
if errors.As(err, &cmderr) {
log.Printf("Got error output: %s", string(cmderr.Stderr))
} else {
log.Printf("Run `bazel %s` to reproduce the failure", shellescape.QuoteCommand(queryArgs))
log.Printf("Run `%s` to reproduce the failure", shellescape.QuoteCommand(cmd.Args))
}
os.Exit(1)
}
Expand Down
6 changes: 6 additions & 0 deletions pkg/sql/execinfrapb/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
load("@rules_proto//proto:defs.bzl", "proto_library")
load("@io_bazel_rules_go//proto:def.bzl", "go_proto_library")
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
load("//pkg/testutils/buildutil:buildutil.bzl", "disallowed_imports_test")

go_library(
name = "execinfrapb",
Expand Down Expand Up @@ -142,3 +143,8 @@ go_proto_library(
"@com_github_gogo_protobuf//gogoproto",
],
)

disallowed_imports_test(
"execinfrapb",
["//pkg/sql/sem/builtins"],
)
64 changes: 64 additions & 0 deletions pkg/testutils/buildutil/buildutil.bzl
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
load("@io_bazel_rules_go//go:def.bzl", "GoLibrary")

# This file contains a single macro disallowed_imports_test which internally
# generates a sh_test which ensures that the label provided as the first arg
# does not import any of the labels provided as a list in the second arg.

# _DepsInfo is used in the _deps_aspect to pick up all the transitive
# dependencies of a go package.
_DepsInfo = provider(
fields = {'deps' : 'depset of targets'}
)

def _deps_aspect_impl(target, ctx):
return [_DepsInfo(
deps = depset(
[target],
transitive = [dep[_DepsInfo].deps for dep in ctx.rule.attr.deps],
)
)]

_deps_aspect = aspect(
implementation = _deps_aspect_impl,
attr_aspects = ['deps'],
provides = [_DepsInfo],
)

def _deps_rule_impl(ctx):
deps = {k: None for k in ctx.attr.src[_DepsInfo].deps.to_list()}
data = ""
failed = [p for p in ctx.attr.disallowed if p in deps]
if failed:
failures = [
"""echo >&2 "ERROR: {0} imports {1}
\tcheck: bazel query 'somepath({0}, {1})'"\
""".format(
ctx.attr.src.label, d.label,
) for d in failed
]
data = "\n".join(failures + ["exit 1"])
f = ctx.actions.declare_file(ctx.attr.name + "_deps_test.sh")
ctx.actions.write(f, data)
return [
DefaultInfo(executable = f),
]

_deps_rule = rule(
implementation = _deps_rule_impl,
executable = True,
attrs = {
'src' : attr.label(aspects = [_deps_aspect], providers = [GoLibrary]),
'disallowed': attr.label_list(providers = [GoLibrary]),
},
)

def disallowed_imports_test(src, disallowed):
script = src.strip(":") + "_disallowed_imports_script"
_deps_rule(name = script, src = src, disallowed = disallowed)
native.sh_test(
name = src.strip(":") + "_disallowed_imports_test",
srcs = [":"+script],
tags = ["local"],
)


0 comments on commit 11d4026

Please sign in to comment.