blob: e9d35068610f835a93b30d071c5be721ad2d7259 [file] [log] [blame]
# Copyright 2022 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.
"""Unit tests for analysis_test.bzl."""
load("@bazel_skylib//rules:common_settings.bzl", "BuildSettingInfo")
load("//lib:analysis_test.bzl", "analysis_test", "test_suite")
load("//lib:truth.bzl", "matching")
load("//lib:util.bzl", "TestingAspectInfo")
_TestingFlagsInfo = provider(
doc = "Flags used for testing",
fields = ["flag_a", "flag_b"],
)
def _testing_flags_impl(ctx):
return [_TestingFlagsInfo(
flag_a = ctx.attr._flag_a[BuildSettingInfo].value,
flag_b = ctx.attr._flag_b[BuildSettingInfo].value,
)]
_testing_flags = rule(
implementation = _testing_flags_impl,
attrs = {
"_flag_a": attr.label(default = "//tests:flag_a"),
"_flag_b": attr.label(default = "//tests:flag_b"),
},
)
_CustomAttributeAspectInfo = provider(doc = "Aspect for a custom attribute", fields = [])
def _custom_attribute_aspect_impl(_target, _ctx):
return [_CustomAttributeAspectInfo()]
_custom_attribute_aspect = aspect(implementation = _custom_attribute_aspect_impl)
###################################
####### change_setting_test #######
###################################
_ChangeSettingInfo = provider(
doc = "min_os_version for change_setting_test",
fields = ["min_os_version"],
)
def _change_setting_fake_rule(ctx):
return [_ChangeSettingInfo(min_os_version = ctx.fragments.cpp.minimum_os_version())]
change_setting_fake_rule = rule(
implementation = _change_setting_fake_rule,
fragments = ["cpp"],
)
def test_change_setting(name):
"""Test to verify that an analysis test may change configuration."""
change_setting_fake_rule(name = name + "_fake_target", tags = ["manual"])
analysis_test(
name = name,
target = name + "_fake_target",
impl = _test_change_setting,
config_settings = {
"//command_line_option:minimum_os_version": "1234.5678",
},
)
def _test_change_setting(env, target):
dep_min_os_version = target[_ChangeSettingInfo].min_os_version
env.expect.that_str(dep_min_os_version).equals("1234.5678")
####################################
####### failure_testing_test #######
####################################
def _failure_testing_fake_rule(_ctx):
fail("This rule should never work")
failure_testing_fake_rule = rule(
implementation = _failure_testing_fake_rule,
)
def test_failure_testing(name):
"""Test to verify that an analysis test may verify a rule fails with fail()."""
failure_testing_fake_rule(name = name + "_fake_target", tags = ["manual"])
analysis_test(
name = name,
target = name + "_fake_target",
impl = _test_failure_testing,
expect_failure = True,
)
def _test_failure_testing(env, target):
env.expect.that_target(target).failures().contains_predicate(matching.contains("This rule should never work"))
############################################
####### fail_unexpected_passing_test #######
############################################
def _fail_unexpected_passing_fake_rule(_ctx):
return []
fail_unexpected_passing_fake_rule = rule(
implementation = _fail_unexpected_passing_fake_rule,
)
# @unused # TODO(ilist): add a shell test checking it fails
def test_fail_unexpected_passing(name):
"""Test that fails by expecting an error that never occurs."""
fail_unexpected_passing_fake_rule(name = name + "_fake_target", tags = ["manual"])
analysis_test(
name = name,
target = name + "_fake_target",
impl = _test_fail_unexpected_passing,
expect_failure = True,
)
def _test_fail_unexpected_passing(env, target):
env.expect.that_target(target).failures().contains_predicate(matching.contains("Oh no, going to fail"))
################################################
####### change_setting_with_failure_test #######
################################################
def _change_setting_with_failure_fake_rule(ctx):
if ctx.fragments.cpp.minimum_os_version() == "error_error":
fail("unexpected minimum_os_version!!!")
return []
change_setting_with_failure_fake_rule = rule(
implementation = _change_setting_with_failure_fake_rule,
fragments = ["cpp"],
)
def test_change_setting_with_failure(name):
change_setting_with_failure_fake_rule(name = name + "_fake_target", tags = ["manual"])
analysis_test(
name = name,
target = name + "_fake_target",
impl = _test_change_setting_with_failure,
expect_failure = True,
config_settings = {
"//command_line_option:minimum_os_version": "error_error",
},
)
def _test_change_setting_with_failure(env, target):
"""Test verifying failure while changing configuration."""
env.expect.that_target(target).failures().contains_predicate(
matching.contains("unexpected minimum_os_version!!!"),
)
####################################
####### inspect_actions_test #######
####################################
def _inspect_actions_fake_rule(ctx):
out_file = ctx.actions.declare_file("out.txt")
ctx.actions.run_shell(
command = "echo 'hello' > %s" % out_file.basename,
outputs = [out_file],
)
return [DefaultInfo(files = depset([out_file]))]
inspect_actions_fake_rule = rule(implementation = _inspect_actions_fake_rule)
def test_inspect_actions(name):
"""Test verifying actions registered by a target."""
inspect_actions_fake_rule(name = name + "_fake_target", tags = ["manual"])
analysis_test(name = name, target = name + "_fake_target", impl = _test_inspect_actions)
def _test_inspect_actions(env, target):
env.expect.that_int(len(target[TestingAspectInfo].actions)).equals(1)
action_output = target[TestingAspectInfo].actions[0].outputs.to_list()[0]
env.expect.that_str(action_output.basename).equals("out.txt")
####################################
####### inspect_aspect_test #######
####################################
_AddedByAspectInfo = provider(
doc = "Example provider added by example aspect",
fields = {"value": "(str)"},
)
def _example_aspect_impl(_target, _ctx):
return [_AddedByAspectInfo(value = "attached by aspect")]
example_aspect = aspect(implementation = _example_aspect_impl)
def _inspect_aspect_fake_rule(ctx):
out_file = ctx.actions.declare_file("out.txt")
ctx.actions.run_shell(
command = "echo 'hello' > %s" % out_file.basename,
outputs = [out_file],
)
return [DefaultInfo(files = depset([out_file]))]
inspect_aspect_fake_rule = rule(implementation = _inspect_aspect_fake_rule)
def test_inspect_aspect(name):
"""Test verifying aspect run on a target."""
inspect_aspect_fake_rule(name = name + "_fake_target", tags = ["manual"])
analysis_test(
name = name,
target = name + "_fake_target",
impl = _test_inspect_aspect,
extra_target_under_test_aspects = [example_aspect],
)
def _test_inspect_aspect(env, target):
env.expect.that_str(target[_AddedByAspectInfo].value).equals("attached by aspect")
########################################
####### inspect_output_dirs_test #######
########################################
_OutputDirInfo = provider(
doc = "bin_path for inspect_output_dirs_test",
fields = ["bin_path"],
)
def _inspect_output_dirs_fake_rule(ctx):
return [_OutputDirInfo(bin_path = ctx.bin_dir.path)]
inspect_output_dirs_fake_rule = rule(implementation = _inspect_output_dirs_fake_rule)
########################################
####### common_attributes_test #######
########################################
def _test_common_attributes(name):
native.filegroup(name = name + "_subject")
_toolchain_template_vars(name = name + "_toolchain_template_vars")
analysis_test(
name = name,
impl = _test_common_attributes_impl,
target = name + "_subject",
attr_values = dict(
features = ["some-feature"],
tags = ["taga", "tagb"],
visibility = ["//visibility:private"],
toolchains = [name + "_toolchain_template_vars"],
# An empty list means "compatible with everything"
target_compatible_with = [],
),
)
def _test_common_attributes_impl(env, target):
_ = target # @unused
ctx = env.ctx
expect = env.expect
expect.that_collection(ctx.attr.tags).contains_at_least(["taga", "tagb"])
expect.that_collection(ctx.attr.features).contains_exactly(["some-feature"])
expect.that_collection(ctx.attr.visibility).contains_exactly([
Label("//visibility:private"),
])
expect.that_collection(ctx.attr.target_compatible_with).contains_exactly([])
expanded = ctx.expand_make_variables("cmd", "$(key)", {})
expect.that_str(expanded).equals("value")
def _toolchain_template_vars_impl(ctx):
_ = ctx # @unused
return [platform_common.TemplateVariableInfo({"key": "value"})]
_toolchain_template_vars = rule(implementation = _toolchain_template_vars_impl)
def _test_custom_target_attributes_common_settings_applied(name):
_testing_flags(
name = name + "_subject",
)
analysis_test(
name = name,
impl = _test_custom_target_attributes_common_settings_applied_impl,
targets = {"subject": name + "_subject"},
config_settings = {
"//tests:flag_a": "999",
},
extra_target_under_test_aspects = [example_aspect],
)
def _test_custom_target_attributes_common_settings_applied_impl(env, targets):
subject = env.expect.that_target(targets.subject)
subject.has_provider(TestingAspectInfo)
subject.has_provider(_AddedByAspectInfo)
flags = targets.subject[_TestingFlagsInfo]
env.expect.that_str(flags.flag_a).equals("999")
def _test_custom_target_attributes_custom_settings_applied(name):
_testing_flags(
name = name + "_subject",
)
analysis_test(
name = name,
impl = _test_custom_target_attributes_custom_settings_applied_impl,
targets = {"subject": name + "_subject"},
attrs = {
"subject": {
"@config_settings": {
"//tests:flag_a": "inner",
},
"aspects": [_custom_attribute_aspect],
},
},
config_settings = {
"//tests:flag_a": "outer",
"//tests:flag_b": "outer",
},
extra_target_under_test_aspects = [example_aspect],
)
def _test_custom_target_attributes_custom_settings_applied_impl(env, targets):
subject = env.expect.that_target(targets.subject)
subject.has_provider(TestingAspectInfo)
subject.has_provider(_AddedByAspectInfo)
subject.has_provider(_CustomAttributeAspectInfo)
flags = targets.subject[_TestingFlagsInfo]
env.expect.that_str(flags.flag_a).equals("inner")
env.expect.that_str(flags.flag_b).equals("outer")
def _test_custom_target_attributes_multiple_complex_attrs(name):
_testing_flags(name = name + "_subject1")
_testing_flags(name = name + "_subject2")
subject1 = name + "_subject1"
subject2 = name + "_subject2"
analysis_test(
name = name,
impl = _test_custom_target_attributes_multiple_complex_attrs_impl,
targets = {
"subject1": subject1,
"subject2": subject2,
"subject_list": [subject1, subject2],
"subject_label_dict": {
subject1: "subject-one",
subject2: "subject-two",
},
},
attrs = {
"subject1": {
"@config_settings": {
"//tests:flag_a": "one",
},
},
"subject2": {
"@config_settings": {
"//tests:flag_a": "two",
},
},
},
)
def _test_custom_target_attributes_multiple_complex_attrs_impl(env, targets):
env.expect.that_target(targets.subject1).has_provider(TestingAspectInfo)
env.expect.that_str(
targets.subject1[_TestingFlagsInfo].flag_a,
).equals("one")
env.expect.that_str(
targets.subject2[_TestingFlagsInfo].flag_a,
).equals("two")
# Target objects can't be compared, so convert them to labels.
env.expect.that_collection([
t.label
for t in targets.subject_list
]).contains_exactly([
targets.subject1.label,
targets.subject2.label,
])
# Target objects can't be compared, so convert them to labels.
env.expect.that_dict({
t.label: v
for t, v in targets.subject_label_dict.items()
}).contains_exactly({
targets.subject1.label: "subject-one",
targets.subject2.label: "subject-two",
})
def _test_target_list_value(name):
native.filegroup(name = name + "_subject1")
native.filegroup(name = name + "_subject2")
analysis_test(
name = name,
target = [name + "_subject1", name + "_subject2"],
impl = _test_target_list_value_impl,
)
def _test_target_list_value_impl(env, targets):
env.expect.that_collection([
t.label
for t in targets
]).contains_exactly([
env.ctx.label.relative(env.ctx.label.name + "_subject1"),
env.ctx.label.relative(env.ctx.label.name + "_subject2"),
])
def _test_target_dict_value(name):
native.filegroup(name = name + "_subject1")
native.filegroup(name = name + "_subject2")
analysis_test(
name = name,
target = {
name + "_subject1": "subject-one",
name + "_subject2": "subject-two",
},
impl = _test_target_dict_value_impl,
)
def _test_target_dict_value_impl(env, targets):
env.expect.that_dict({
t.label: v
for t, v in targets.items()
}).contains_exactly({
env.ctx.label.relative(env.ctx.label.name + "_subject1"): "subject-one",
env.ctx.label.relative(env.ctx.label.name + "_subject2"): "subject-two",
})
def analysis_test_test_suite(name):
test_suite(
name = name,
tests = [
test_change_setting,
_test_common_attributes,
_test_custom_target_attributes_common_settings_applied,
_test_custom_target_attributes_custom_settings_applied,
_test_custom_target_attributes_multiple_complex_attrs,
_test_target_list_value,
_test_target_dict_value,
test_failure_testing,
test_change_setting_with_failure,
test_inspect_actions,
test_inspect_aspect,
],
)