| # Copyright 2025 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. |
| |
| """Tests for attr_builders.""" |
| |
| load("@rules_testing//lib:analysis_test.bzl", "analysis_test") |
| load("@rules_testing//lib:test_suite.bzl", "test_suite") |
| load("@rules_testing//lib:truth.bzl", "truth") |
| load("//python/private:attr_builders.bzl", "attrb") # buildifier: disable=bzl-visibility |
| load("//python/private:common_labels.bzl", "labels") # buildifier: disable=bzl-visibility |
| |
| def _expect_cfg_defaults(expect, cfg): |
| expect.where(expr = "cfg.outputs").that_collection(cfg.outputs()).contains_exactly([]) |
| expect.where(expr = "cfg.inputs").that_collection(cfg.inputs()).contains_exactly([]) |
| expect.where(expr = "cfg.implementation").that_bool(cfg.implementation()).equals(None) |
| expect.where(expr = "cfg.target").that_bool(cfg.target()).equals(True) |
| expect.where(expr = "cfg.exec_group").that_str(cfg.exec_group()).equals(None) |
| expect.where(expr = "cfg.which_cfg").that_str(cfg.which_cfg()).equals("target") |
| |
| _some_aspect = aspect(implementation = lambda target, ctx: None) |
| _SomeInfo = provider("MyInfo", fields = []) |
| |
| _tests = [] |
| |
| def _report_failures(name, env): |
| failures = env.failures |
| |
| def _report_failures_impl(env, target): |
| _ = target # @unused |
| env._failures.extend(failures) |
| |
| analysis_test( |
| name = name, |
| target = labels.NONE, |
| impl = _report_failures_impl, |
| ) |
| |
| # Calling attr.xxx() outside of the loading phase is an error, but rules_testing |
| # creates the expect/truth helpers during the analysis phase. To make the truth |
| # helpers available during the loading phase, fake out the ctx just enough to |
| # satify rules_testing. |
| def _loading_phase_expect(test_name): |
| env = struct( |
| ctx = struct( |
| workspace_name = "bogus", |
| label = Label(test_name), |
| attr = struct( |
| _impl_name = test_name, |
| ), |
| ), |
| failures = [], |
| ) |
| return env, truth.expect(env) |
| |
| def _expect_builds(expect, builder, attribute_type): |
| expect.that_str(str(builder.build())).contains(attribute_type) |
| |
| def _test_cfg_arg(name): |
| env, _ = _loading_phase_expect(name) |
| |
| def build_cfg(cfg): |
| attrb.Label(cfg = cfg).build() |
| |
| build_cfg(None) |
| build_cfg("target") |
| build_cfg("exec") |
| build_cfg(dict(exec_group = "eg")) |
| build_cfg(dict(implementation = (lambda settings, attr: None))) |
| build_cfg(config.exec()) |
| build_cfg(transition( |
| implementation = (lambda settings, attr: None), |
| inputs = [], |
| outputs = [], |
| )) |
| |
| # config.target is Bazel 8+ |
| if hasattr(config, "target"): |
| build_cfg(config.target()) |
| |
| # config.none is Bazel 8+ |
| if hasattr(config, "none"): |
| build_cfg("none") |
| build_cfg(config.none()) |
| |
| _report_failures(name, env) |
| |
| _tests.append(_test_cfg_arg) |
| |
| def _test_bool(name): |
| env, expect = _loading_phase_expect(name) |
| subject = attrb.Bool() |
| expect.that_str(subject.doc()).equals("") |
| expect.that_bool(subject.default()).equals(False) |
| expect.that_bool(subject.mandatory()).equals(False) |
| _expect_builds(expect, subject, "attr.bool") |
| |
| subject.set_default(True) |
| subject.set_mandatory(True) |
| subject.set_doc("doc") |
| |
| expect.that_str(subject.doc()).equals("doc") |
| expect.that_bool(subject.default()).equals(True) |
| expect.that_bool(subject.mandatory()).equals(True) |
| _expect_builds(expect, subject, "attr.bool") |
| |
| _report_failures(name, env) |
| |
| _tests.append(_test_bool) |
| |
| def _test_int(name): |
| env, expect = _loading_phase_expect(name) |
| |
| subject = attrb.Int() |
| expect.that_int(subject.default()).equals(0) |
| expect.that_str(subject.doc()).equals("") |
| expect.that_bool(subject.mandatory()).equals(False) |
| expect.that_collection(subject.values()).contains_exactly([]) |
| _expect_builds(expect, subject, "attr.int") |
| |
| subject.set_default(42) |
| subject.set_doc("doc") |
| subject.set_mandatory(True) |
| subject.values().append(42) |
| |
| expect.that_int(subject.default()).equals(42) |
| expect.that_str(subject.doc()).equals("doc") |
| expect.that_bool(subject.mandatory()).equals(True) |
| expect.that_collection(subject.values()).contains_exactly([42]) |
| _expect_builds(expect, subject, "attr.int") |
| |
| _report_failures(name, env) |
| |
| _tests.append(_test_int) |
| |
| def _test_int_list(name): |
| env, expect = _loading_phase_expect(name) |
| |
| subject = attrb.IntList() |
| expect.that_bool(subject.allow_empty()).equals(True) |
| expect.that_collection(subject.default()).contains_exactly([]) |
| expect.that_str(subject.doc()).equals("") |
| expect.that_bool(subject.mandatory()).equals(False) |
| _expect_builds(expect, subject, "attr.int_list") |
| |
| subject.default().append(99) |
| subject.set_doc("doc") |
| subject.set_mandatory(True) |
| |
| expect.that_collection(subject.default()).contains_exactly([99]) |
| expect.that_str(subject.doc()).equals("doc") |
| expect.that_bool(subject.mandatory()).equals(True) |
| _expect_builds(expect, subject, "attr.int_list") |
| |
| _report_failures(name, env) |
| |
| _tests.append(_test_int_list) |
| |
| def _test_label(name): |
| env, expect = _loading_phase_expect(name) |
| |
| subject = attrb.Label() |
| |
| expect.that_str(subject.default()).equals(None) |
| expect.that_str(subject.doc()).equals("") |
| expect.that_bool(subject.mandatory()).equals(False) |
| expect.that_bool(subject.executable()).equals(False) |
| expect.that_bool(subject.allow_files()).equals(None) |
| expect.that_bool(subject.allow_single_file()).equals(None) |
| expect.that_collection(subject.providers()).contains_exactly([]) |
| expect.that_collection(subject.aspects()).contains_exactly([]) |
| _expect_cfg_defaults(expect, subject.cfg) |
| _expect_builds(expect, subject, "attr.label") |
| |
| subject.set_default("//foo:bar") |
| subject.set_doc("doc") |
| subject.set_mandatory(True) |
| subject.set_executable(True) |
| subject.add_allow_files(".txt") |
| subject.cfg.set_target() |
| subject.providers().append(_SomeInfo) |
| subject.aspects().append(_some_aspect) |
| subject.cfg.outputs().append(Label("//some:output")) |
| subject.cfg.inputs().append(Label("//some:input")) |
| impl = lambda: None |
| subject.cfg.set_implementation(impl) |
| |
| expect.that_str(subject.default()).equals("//foo:bar") |
| expect.that_str(subject.doc()).equals("doc") |
| expect.that_bool(subject.mandatory()).equals(True) |
| expect.that_bool(subject.executable()).equals(True) |
| expect.that_collection(subject.allow_files()).contains_exactly([".txt"]) |
| expect.that_bool(subject.allow_single_file()).equals(None) |
| expect.that_collection(subject.providers()).contains_exactly([_SomeInfo]) |
| expect.that_collection(subject.aspects()).contains_exactly([_some_aspect]) |
| expect.that_collection(subject.cfg.outputs()).contains_exactly([Label("//some:output")]) |
| expect.that_collection(subject.cfg.inputs()).contains_exactly([Label("//some:input")]) |
| expect.that_bool(subject.cfg.implementation()).equals(impl) |
| _expect_builds(expect, subject, "attr.label") |
| |
| _report_failures(name, env) |
| |
| _tests.append(_test_label) |
| |
| def _test_label_keyed_string_dict(name): |
| env, expect = _loading_phase_expect(name) |
| |
| subject = attrb.LabelKeyedStringDict() |
| |
| expect.that_dict(subject.default()).contains_exactly({}) |
| expect.that_str(subject.doc()).equals("") |
| expect.that_bool(subject.mandatory()).equals(False) |
| expect.that_bool(subject.allow_files()).equals(False) |
| expect.that_collection(subject.providers()).contains_exactly([]) |
| expect.that_collection(subject.aspects()).contains_exactly([]) |
| _expect_cfg_defaults(expect, subject.cfg) |
| _expect_builds(expect, subject, "attr.label_keyed_string_dict") |
| |
| subject.default()["key"] = "//some:label" |
| subject.set_doc("doc") |
| subject.set_mandatory(True) |
| subject.set_allow_files(True) |
| subject.cfg.set_target() |
| subject.providers().append(_SomeInfo) |
| subject.aspects().append(_some_aspect) |
| subject.cfg.outputs().append("//some:output") |
| subject.cfg.inputs().append("//some:input") |
| impl = lambda: None |
| subject.cfg.set_implementation(impl) |
| |
| expect.that_dict(subject.default()).contains_exactly({"key": "//some:label"}) |
| expect.that_str(subject.doc()).equals("doc") |
| expect.that_bool(subject.mandatory()).equals(True) |
| expect.that_bool(subject.allow_files()).equals(True) |
| expect.that_collection(subject.providers()).contains_exactly([_SomeInfo]) |
| expect.that_collection(subject.aspects()).contains_exactly([_some_aspect]) |
| expect.that_collection(subject.cfg.outputs()).contains_exactly(["//some:output"]) |
| expect.that_collection(subject.cfg.inputs()).contains_exactly(["//some:input"]) |
| expect.that_bool(subject.cfg.implementation()).equals(impl) |
| |
| _expect_builds(expect, subject, "attr.label_keyed_string_dict") |
| |
| subject.add_allow_files(".txt") |
| expect.that_collection(subject.allow_files()).contains_exactly([".txt"]) |
| _expect_builds(expect, subject, "attr.label_keyed_string_dict") |
| |
| _report_failures(name, env) |
| |
| _tests.append(_test_label_keyed_string_dict) |
| |
| def _test_label_list(name): |
| env, expect = _loading_phase_expect(name) |
| |
| subject = attrb.LabelList() |
| |
| expect.that_collection(subject.default()).contains_exactly([]) |
| expect.that_str(subject.doc()).equals("") |
| expect.that_bool(subject.mandatory()).equals(False) |
| expect.that_bool(subject.allow_files()).equals(False) |
| expect.that_collection(subject.providers()).contains_exactly([]) |
| expect.that_collection(subject.aspects()).contains_exactly([]) |
| _expect_cfg_defaults(expect, subject.cfg) |
| _expect_builds(expect, subject, "attr.label_list") |
| |
| subject.default().append("//some:label") |
| subject.set_doc("doc") |
| subject.set_mandatory(True) |
| subject.set_allow_files([".txt"]) |
| subject.providers().append(_SomeInfo) |
| subject.aspects().append(_some_aspect) |
| |
| expect.that_collection(subject.default()).contains_exactly(["//some:label"]) |
| expect.that_str(subject.doc()).equals("doc") |
| expect.that_bool(subject.mandatory()).equals(True) |
| expect.that_collection(subject.allow_files()).contains_exactly([".txt"]) |
| expect.that_collection(subject.providers()).contains_exactly([_SomeInfo]) |
| expect.that_collection(subject.aspects()).contains_exactly([_some_aspect]) |
| |
| _expect_builds(expect, subject, "attr.label_list") |
| |
| _report_failures(name, env) |
| |
| _tests.append(_test_label_list) |
| |
| def _test_output(name): |
| env, expect = _loading_phase_expect(name) |
| |
| subject = attrb.Output() |
| expect.that_str(subject.doc()).equals("") |
| expect.that_bool(subject.mandatory()).equals(False) |
| _expect_builds(expect, subject, "attr.output") |
| |
| subject.set_doc("doc") |
| subject.set_mandatory(True) |
| expect.that_str(subject.doc()).equals("doc") |
| expect.that_bool(subject.mandatory()).equals(True) |
| _expect_builds(expect, subject, "attr.output") |
| |
| _report_failures(name, env) |
| |
| _tests.append(_test_output) |
| |
| def _test_output_list(name): |
| env, expect = _loading_phase_expect(name) |
| |
| subject = attrb.OutputList() |
| expect.that_bool(subject.allow_empty()).equals(True) |
| expect.that_str(subject.doc()).equals("") |
| expect.that_bool(subject.mandatory()).equals(False) |
| _expect_builds(expect, subject, "attr.output_list") |
| |
| subject.set_allow_empty(False) |
| subject.set_doc("doc") |
| subject.set_mandatory(True) |
| expect.that_bool(subject.allow_empty()).equals(False) |
| expect.that_str(subject.doc()).equals("doc") |
| expect.that_bool(subject.mandatory()).equals(True) |
| _expect_builds(expect, subject, "attr.output_list") |
| |
| _report_failures(name, env) |
| |
| _tests.append(_test_output_list) |
| |
| def _test_string(name): |
| env, expect = _loading_phase_expect(name) |
| |
| subject = attrb.String() |
| expect.that_str(subject.default()).equals("") |
| expect.that_str(subject.doc()).equals("") |
| expect.that_bool(subject.mandatory()).equals(False) |
| expect.that_collection(subject.values()).contains_exactly([]) |
| _expect_builds(expect, subject, "attr.string") |
| |
| subject.set_doc("doc") |
| subject.set_mandatory(True) |
| subject.values().append("green") |
| expect.that_str(subject.doc()).equals("doc") |
| expect.that_bool(subject.mandatory()).equals(True) |
| expect.that_collection(subject.values()).contains_exactly(["green"]) |
| _expect_builds(expect, subject, "attr.string") |
| |
| _report_failures(name, env) |
| |
| _tests.append(_test_string) |
| |
| def _test_string_dict(name): |
| env, expect = _loading_phase_expect(name) |
| |
| subject = attrb.StringDict() |
| |
| expect.that_dict(subject.default()).contains_exactly({}) |
| expect.that_str(subject.doc()).equals("") |
| expect.that_bool(subject.mandatory()).equals(False) |
| expect.that_bool(subject.allow_empty()).equals(True) |
| _expect_builds(expect, subject, "attr.string_dict") |
| |
| subject.default()["key"] = "value" |
| subject.set_doc("doc") |
| subject.set_mandatory(True) |
| subject.set_allow_empty(False) |
| |
| expect.that_dict(subject.default()).contains_exactly({"key": "value"}) |
| expect.that_str(subject.doc()).equals("doc") |
| expect.that_bool(subject.mandatory()).equals(True) |
| expect.that_bool(subject.allow_empty()).equals(False) |
| _expect_builds(expect, subject, "attr.string_dict") |
| |
| _report_failures(name, env) |
| |
| _tests.append(_test_string_dict) |
| |
| def _test_string_keyed_label_dict(name): |
| env, expect = _loading_phase_expect(name) |
| |
| subject = attrb.StringKeyedLabelDict() |
| |
| expect.that_dict(subject.default()).contains_exactly({}) |
| expect.that_str(subject.doc()).equals("") |
| expect.that_bool(subject.mandatory()).equals(False) |
| expect.that_bool(subject.allow_files()).equals(False) |
| expect.that_collection(subject.providers()).contains_exactly([]) |
| expect.that_collection(subject.aspects()).contains_exactly([]) |
| _expect_cfg_defaults(expect, subject.cfg) |
| _expect_builds(expect, subject, "attr.string_keyed_label_dict") |
| |
| subject.default()["key"] = "//some:label" |
| subject.set_doc("doc") |
| subject.set_mandatory(True) |
| subject.set_allow_files([".txt"]) |
| subject.providers().append(_SomeInfo) |
| subject.aspects().append(_some_aspect) |
| |
| expect.that_dict(subject.default()).contains_exactly({"key": "//some:label"}) |
| expect.that_str(subject.doc()).equals("doc") |
| expect.that_bool(subject.mandatory()).equals(True) |
| expect.that_collection(subject.allow_files()).contains_exactly([".txt"]) |
| expect.that_collection(subject.providers()).contains_exactly([_SomeInfo]) |
| expect.that_collection(subject.aspects()).contains_exactly([_some_aspect]) |
| |
| _expect_builds(expect, subject, "attr.string_keyed_label_dict") |
| |
| _report_failures(name, env) |
| |
| _tests.append(_test_string_keyed_label_dict) |
| |
| def _test_string_list(name): |
| env, expect = _loading_phase_expect(name) |
| |
| subject = attrb.StringList() |
| |
| expect.that_collection(subject.default()).contains_exactly([]) |
| expect.that_str(subject.doc()).equals("") |
| expect.that_bool(subject.mandatory()).equals(False) |
| expect.that_bool(subject.allow_empty()).equals(True) |
| _expect_builds(expect, subject, "attr.string_list") |
| |
| subject.set_doc("doc") |
| subject.set_mandatory(True) |
| subject.default().append("blue") |
| subject.set_allow_empty(False) |
| expect.that_str(subject.doc()).equals("doc") |
| expect.that_bool(subject.mandatory()).equals(True) |
| expect.that_bool(subject.allow_empty()).equals(False) |
| expect.that_collection(subject.default()).contains_exactly(["blue"]) |
| _expect_builds(expect, subject, "attr.string_list") |
| |
| _report_failures(name, env) |
| |
| _tests.append(_test_string_list) |
| |
| def _test_string_list_dict(name): |
| env, expect = _loading_phase_expect(name) |
| |
| subject = attrb.StringListDict() |
| |
| expect.that_dict(subject.default()).contains_exactly({}) |
| expect.that_str(subject.doc()).equals("") |
| expect.that_bool(subject.mandatory()).equals(False) |
| expect.that_bool(subject.allow_empty()).equals(True) |
| _expect_builds(expect, subject, "attr.string_list_dict") |
| |
| subject.set_doc("doc") |
| subject.set_mandatory(True) |
| subject.default()["key"] = ["red"] |
| subject.set_allow_empty(False) |
| expect.that_str(subject.doc()).equals("doc") |
| expect.that_bool(subject.mandatory()).equals(True) |
| expect.that_bool(subject.allow_empty()).equals(False) |
| expect.that_dict(subject.default()).contains_exactly({"key": ["red"]}) |
| _expect_builds(expect, subject, "attr.string_list_dict") |
| |
| _report_failures(name, env) |
| |
| _tests.append(_test_string_list_dict) |
| |
| def attr_builders_test_suite(name): |
| test_suite( |
| name = name, |
| tests = _tests, |
| ) |