Test proto_common.compile

Rewrite compile tests from BazelProtoCommonTest to Starlark. This is using rules_analysis for testing.

The tests are super fast (cca. 1s for all of the to run).

The tests work either with a redirect (calling native rule) or with actual implementation in the protobuf repository.

PiperOrigin-RevId: 651748083
pull/18339/head
Protobuf Team Bot 5 months ago committed by Joshua Haberman
parent d9fb893ba7
commit dc17de7c7b
  1. 3
      MODULE.bazel
  2. 9
      WORKSPACE
  3. 3
      bazel/tests/BUILD
  4. 361
      bazel/tests/proto_common_compile_tests.bzl
  5. 130
      bazel/tests/testdata/BUILD
  6. 50
      bazel/tests/testdata/compile_rule.bzl

@ -71,3 +71,6 @@ crate.spec(
)
crate.from_specs()
use_repo(crate, crate_index = "crates")
# Development dependencies
bazel_dep(name = "rules_testing", version = "0.6.0", dev_dependency = True)

@ -241,3 +241,12 @@ http_archive(
# Needed as a dependency of @com_google_protobuf_v25.0
load("@com_google_protobuf_v25.0//:protobuf_deps.bzl", protobuf_v25_deps="protobuf_deps")
protobuf_v25_deps()
# Needed for testing only
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
http_archive(
name = "rules_testing",
sha256 = "02c62574631876a4e3b02a1820cb51167bb9cdcdea2381b2fa9d9b8b11c407c4",
strip_prefix = "rules_testing-0.6.0",
url = "https://github.com/bazelbuild/rules_testing/releases/download/v0.6.0/rules_testing-v0.6.0.tar.gz",
)

@ -0,0 +1,3 @@
load(":proto_common_compile_tests.bzl", "proto_common_compile_test_suite")
proto_common_compile_test_suite(name = "proto_common_compile_test_suite")

@ -0,0 +1,361 @@
"""Tests for `proto_common.compile` function."""
load("@rules_testing//lib:analysis_test.bzl", "analysis_test", "test_suite")
load("@rules_testing//lib:truth.bzl", "matching")
load("@rules_testing//lib:util.bzl", "util")
load("//bazel:proto_library.bzl", "proto_library")
load("//bazel/tests/testdata:compile_rule.bzl", "compile_rule")
protocol_compiler = "/protoc"
def proto_common_compile_test_suite(name):
util.helper_target(
proto_library,
name = "simple_proto",
srcs = ["A.proto"],
)
test_suite(
name = name,
tests = [
_test_compile_basic,
_test_compile_noplugin,
_test_compile_with_plugin_output,
_test_compile_with_directory_plugin_output,
_test_compile_additional_args,
_test_compile_additional_tools,
_test_compile_additional_tools_no_plugin,
_test_compile_additional_inputs,
_test_compile_resource_set,
_test_compile_protoc_opts,
_test_compile_direct_generated_protos,
_test_compile_indirect_generated_protos,
],
)
# Verifies basic usage of `proto_common.compile`.
def _test_compile_basic(name):
util.helper_target(
compile_rule,
name = name + "_compile",
proto_dep = ":simple_proto",
)
analysis_test(
name = name,
target = name + "_compile",
impl = _test_compile_basic_impl,
)
def _test_compile_basic_impl(env, target):
action = env.expect.that_target(target).action_named("MyMnemonic")
action.argv().contains_exactly_predicates(
[
matching.str_endswith(protocol_compiler),
matching.str_matches("--plugin=b*-out/*-exec-*/bin/*/testdata/plugin"),
matching.equals_wrapper("-I."),
matching.str_endswith("/A.proto"),
],
)
action.mnemonic().equals("MyMnemonic")
# Verifies usage of proto_common.generate_code with no plugin specified by toolchain.
def _test_compile_noplugin(name):
util.helper_target(
compile_rule,
name = name + "_compile",
proto_dep = ":simple_proto",
toolchain = "//bazel/tests/testdata:toolchain_noplugin",
)
analysis_test(
name = name,
target = name + "_compile",
impl = _test_compile_noplugin_impl,
)
def _test_compile_noplugin_impl(env, target):
action = env.expect.that_target(target).action_named("MyMnemonic")
action.argv().contains_exactly_predicates(
[
matching.str_endswith(protocol_compiler),
matching.equals_wrapper("-I."),
matching.str_endswith("/A.proto"),
],
)
# Verifies usage of `proto_common.compile` with `plugin_output` parameter set to file.
def _test_compile_with_plugin_output(name):
util.helper_target(
compile_rule,
name = name + "_compile",
proto_dep = ":simple_proto",
plugin_output = "single",
)
analysis_test(
name = name,
target = name + "_compile",
impl = _test_compile_with_plugin_output_impl,
)
def _test_compile_with_plugin_output_impl(env, target):
action = env.expect.that_target(target).action_named("MyMnemonic")
action.argv().contains_exactly_predicates(
[
matching.str_endswith(protocol_compiler),
matching.str_matches("--java_out=param1,param2:b*-out/*/test_compile_with_plugin_output_compile"),
matching.str_matches("--plugin=b*-out/*-exec-*/bin/*/testdata/plugin"),
matching.equals_wrapper("-I."),
matching.str_endswith("/A.proto"),
],
)
# Verifies usage of `proto_common.compile` with `plugin_output` parameter set to file.
def _test_compile_with_directory_plugin_output(name):
util.helper_target(
compile_rule,
name = name + "_compile",
proto_dep = ":simple_proto",
plugin_output = "multiple",
)
analysis_test(
name = name,
target = name + "_compile",
impl = _test_compile_with_directory_plugin_output_impl,
)
def _test_compile_with_directory_plugin_output_impl(env, target):
action = env.expect.that_target(target).action_named("MyMnemonic")
action.argv().contains_exactly_predicates(
[
matching.str_endswith(protocol_compiler),
matching.str_matches("--java_out=param1,param2:b*-out/*/bin"),
matching.str_matches("--plugin=b*-out/*-exec-*/bin/*/testdata/plugin"),
matching.equals_wrapper("-I."),
matching.str_endswith("/A.proto"),
],
)
# Verifies usage of `proto_common.compile` with `additional_args` parameter
def _test_compile_additional_args(name):
util.helper_target(
compile_rule,
name = name + "_compile",
proto_dep = ":simple_proto",
additional_args = ["--a", "--b"],
)
analysis_test(
name = name,
target = name + "_compile",
impl = _test_compile_additional_args_impl,
)
def _test_compile_additional_args_impl(env, target):
action = env.expect.that_target(target).action_named("MyMnemonic")
action.argv().contains_exactly_predicates(
[
matching.str_endswith(protocol_compiler),
matching.equals_wrapper("--a"),
matching.equals_wrapper("--b"),
matching.str_matches("--plugin=b*-out/*-exec-*/bin/*/testdata/plugin"),
matching.equals_wrapper("-I."),
matching.str_endswith("/A.proto"),
],
)
# Verifies usage of `proto_common.compile` with `additional_tools` parameter
def _test_compile_additional_tools(name):
util.helper_target(
compile_rule,
name = name + "_compile",
proto_dep = ":simple_proto",
additional_tools = [
"//bazel/tests/testdata:_tool1",
"//bazel/tests/testdata:_tool2",
],
)
analysis_test(
name = name,
target = name + "_compile",
impl = _test_compile_additional_tools_impl,
)
def _test_compile_additional_tools_impl(env, target):
action = env.expect.that_target(target).action_named("MyMnemonic")
action.inputs().contains_at_least_predicates(
[
matching.file_basename_equals("_tool1"),
matching.file_basename_equals("_tool2"),
matching.file_basename_equals("plugin"),
],
)
# Verifies usage of `proto_common.compile` with `additional_tools` parameter and no plugin on the toolchain.
def _test_compile_additional_tools_no_plugin(name):
util.helper_target(
compile_rule,
name = name + "_compile",
proto_dep = ":simple_proto",
additional_tools = [
"//bazel/tests/testdata:_tool1",
"//bazel/tests/testdata:_tool2",
],
toolchain = "//bazel/tests/testdata:toolchain_noplugin",
)
analysis_test(
name = name,
target = name + "_compile",
impl = _test_compile_additional_tools_no_plugin_impl,
)
def _test_compile_additional_tools_no_plugin_impl(env, target):
action = env.expect.that_target(target).action_named("MyMnemonic")
action.inputs().contains_at_least_predicates(
[
matching.file_basename_equals("_tool1"),
matching.file_basename_equals("_tool2"),
],
)
action.inputs().not_contains_predicate(matching.file_basename_equals("plugin"))
# Verifies usage of `proto_common.compile` with `additional_inputs` parameter.
def _test_compile_additional_inputs(name):
util.helper_target(
compile_rule,
name = name + "_compile",
proto_dep = ":simple_proto",
additional_inputs = ["input1.txt", "input2.txt"],
)
analysis_test(
name = name,
target = name + "_compile",
impl = _test_compile_additional_inputs_impl,
)
def _test_compile_additional_inputs_impl(env, target):
action = env.expect.that_target(target).action_named("MyMnemonic")
action.inputs().contains_at_least_predicates(
[
matching.file_basename_equals("input1.txt"),
matching.file_basename_equals("input2.txt"),
],
)
# Verifies usage of `proto_common.compile` with `additional_tools` parameter and no plugin on the toolchain.
def _test_compile_resource_set(name):
util.helper_target(
compile_rule,
name = name + "_compile",
proto_dep = ":simple_proto",
use_resource_set = True,
)
analysis_test(
name = name,
target = name + "_compile",
impl = _test_compile_resource_set_impl,
)
def _test_compile_resource_set_impl(env, target):
action = env.expect.that_target(target).action_named("MyMnemonic") # @unused
# We can't check the specification of the resource set, but we at least verify analysis passes
# Verifies `--protocopts` are passed to command line.
def _test_compile_protoc_opts(name):
util.helper_target(
compile_rule,
name = name + "_compile",
proto_dep = ":simple_proto",
)
analysis_test(
name = name,
target = name + "_compile",
config_settings = {"//command_line_option:protocopt": ["--foo", "--bar"]},
impl = _test_compile_protoc_opts_impl,
)
def _test_compile_protoc_opts_impl(env, target):
action = env.expect.that_target(target).action_named("MyMnemonic")
action.argv().contains_exactly_predicates(
[
matching.str_endswith(protocol_compiler),
matching.equals_wrapper("--foo"),
matching.equals_wrapper("--bar"),
matching.str_matches("--plugin=b*-out/*-exec-*/bin/*/testdata/plugin"),
matching.equals_wrapper("-I."),
matching.str_endswith("/A.proto"),
],
)
# Verifies `proto_common.compile`> correctly handles direct generated `.proto` files.
def _test_compile_direct_generated_protos(name):
util.helper_target(native.genrule, name = name + "_generate_G", cmd = "", outs = ["G.proto"])
util.helper_target(
proto_library,
name = name + "_directly_generated_proto",
srcs = ["A.proto", "G.proto"],
)
util.helper_target(
compile_rule,
name = name + "_compile",
proto_dep = name + "_directly_generated_proto",
)
analysis_test(
name = name,
target = name + "_compile",
impl = _test_compile_direct_generated_protos_impl,
)
def _test_compile_direct_generated_protos_impl(env, target):
action = env.expect.that_target(target).action_named("MyMnemonic")
action.argv().contains_exactly_predicates(
[
matching.str_endswith(protocol_compiler),
matching.str_matches("--plugin=b*-out/*-exec-*/bin/*/testdata/plugin"),
matching.str_matches("-Ib*-out/*/*"),
matching.equals_wrapper("-I."),
matching.str_endswith("/A.proto"),
matching.str_matches("*-out/*/*/*/G.proto"),
],
)
# Verifies usage of `proto_common.compile` with `plugin_output` parameter
def _test_compile_indirect_generated_protos(name):
util.helper_target(native.genrule, name = "_generate_h", srcs = ["A.txt"], cmd = "", outs = ["H.proto"])
util.helper_target(proto_library, name = "_generated_proto", srcs = ["H.proto"])
util.helper_target(
proto_library,
name = name + "_indirectly_generated_proto",
srcs = ["A.proto"],
deps = [":_generated_proto"],
)
util.helper_target(
compile_rule,
name = name + "_compile",
proto_dep = name + "_indirectly_generated_proto",
)
analysis_test(
name = name,
target = name + "_compile",
impl = _test_compile_indirect_generated_protos_impl,
)
def _test_compile_indirect_generated_protos_impl(env, target):
action = env.expect.that_target(target).action_named("MyMnemonic")
action.argv().contains_exactly_predicates(
[
matching.str_endswith(protocol_compiler),
matching.str_matches("--plugin=b*-out/*-exec-*/bin/*/testdata/plugin"),
matching.str_matches("-Ib*-out/*/*"),
matching.equals_wrapper("-I."),
matching.str_endswith("/A.proto"),
],
)

@ -0,0 +1,130 @@
package(default_visibility = ["//visibility:public"])
proto_lang_toolchain(
name = "toolchain",
blacklisted_protos = [":denied"],
command_line = "--java_out=param1,param2:$(OUT)",
mnemonic = "MyMnemonic",
plugin = ":plugin",
plugin_format_flag = "--plugin=%s",
progress_message = "Progress Message %{label}",
runtime = ":runtime",
tags = [
"manual",
"nobuilder",
"notap",
],
)
proto_lang_toolchain(
name = "toolchain_noplugin",
blacklisted_protos = [":denied"],
command_line = "--java_out=param1,param2:$(OUT)",
mnemonic = "MyMnemonic",
progress_message = "Progress Message %{label}",
runtime = ":runtime",
tags = [
"manual",
"nobuilder",
"notap",
],
)
cc_binary(
name = "plugin",
srcs = ["plugin.cc"],
tags = [
"manual",
"nobuilder",
"notap",
],
)
cc_library(
name = "runtime",
srcs = ["runtime.cc"],
tags = [
"manual",
"nobuilder",
"notap",
],
)
filegroup(
name = "descriptors",
srcs = [
"descriptor.proto",
"metadata.proto",
],
tags = [
"manual",
"nobuilder",
"notap",
],
)
filegroup(
name = "any",
srcs = ["any.proto"],
tags = [
"manual",
"nobuilder",
"notap",
],
)
filegroup(
name = "something",
srcs = ["something.proto"],
tags = [
"manual",
"nobuilder",
"notap",
],
)
proto_library(
name = "mixed",
srcs = [
":descriptors",
":something",
],
tags = [
"manual",
"nobuilder",
"notap",
],
)
proto_library(
name = "denied",
srcs = [
":any",
":descriptors",
],
tags = [
"manual",
"nobuilder",
"notap",
],
)
cc_binary(
name = "_tool1",
srcs = ["tool1.cc"],
tags = [
"manual",
"nobuilder",
"notap",
],
)
cc_binary(
name = "_tool2",
srcs = ["tool2.cc"],
tags = [
"manual",
"nobuilder",
"notap",
],
)

@ -0,0 +1,50 @@
"""Testing function for proto_common module"""
load("//bazel/common:proto_common.bzl", "proto_common")
def _resource_set_callback(_os, inputs_size):
return {"memory": 25 + 0.15 * inputs_size, "cpu": 1}
def _impl(ctx):
outfile = ctx.actions.declare_file(ctx.attr.name)
kwargs = {}
if ctx.attr.plugin_output == "single":
kwargs["plugin_output"] = outfile.path
elif ctx.attr.plugin_output == "multiple":
kwargs["plugin_output"] = ctx.bin_dir.path
elif ctx.attr.plugin_output == "wrong":
kwargs["plugin_output"] = ctx.bin_dir.path + "///"
if ctx.attr.additional_args:
additional_args = ctx.actions.args()
additional_args.add_all(ctx.attr.additional_args)
kwargs["additional_args"] = additional_args
if ctx.files.additional_tools:
kwargs["additional_tools"] = ctx.files.additional_tools
if ctx.files.additional_inputs:
kwargs["additional_inputs"] = depset(ctx.files.additional_inputs)
if ctx.attr.use_resource_set:
kwargs["resource_set"] = _resource_set_callback
if ctx.attr.progress_message:
kwargs["experimental_progress_message"] = ctx.attr.progress_message
proto_common.compile(
ctx.actions,
ctx.attr.proto_dep[ProtoInfo],
ctx.attr.toolchain[proto_common.ProtoLangToolchainInfo],
[outfile],
**kwargs
)
return [DefaultInfo(files = depset([outfile]))]
compile_rule = rule(
_impl,
attrs = {
"proto_dep": attr.label(),
"plugin_output": attr.string(),
"toolchain": attr.label(default = ":toolchain"),
"additional_args": attr.string_list(),
"additional_tools": attr.label_list(cfg = "exec"),
"additional_inputs": attr.label_list(allow_files = True),
"use_resource_set": attr.bool(),
"progress_message": attr.string(),
},
)
Loading…
Cancel
Save