| """This module contains unit tests for rust_proto_library and its aspect.""" |
| |
| load("@bazel_skylib//lib:unittest.bzl", "analysistest", "asserts") |
| load("//rust:aspects.bzl", "RustProtoInfo") |
| load("//rust:defs.bzl", "rust_cc_proto_library", "rust_upb_proto_library") |
| load(":defs.bzl", "ActionsInfo", "attach_cc_aspect", "attach_upb_aspect") |
| |
| def _find_actions_with_mnemonic(actions, mnemonic): |
| actions = [a for a in actions if a.mnemonic == mnemonic] |
| if not actions: |
| fail("Couldn't find action with mnemonic {} among {}".format( |
| mnemonic, |
| [a.mnemonic for a in actions], |
| )) |
| return actions |
| |
| def _check_crate_mapping(actions, target_name): |
| fw_actions = _find_actions_with_mnemonic(actions, "FileWrite") |
| crate_mapping_action = None |
| for a in fw_actions: |
| outputs = a.outputs.to_list() |
| output = [o for o in outputs if o.basename == target_name + ".rust_crate_mapping"] |
| if output: |
| crate_mapping_action = a |
| if not crate_mapping_action: |
| fail("Couldn't find action outputting {}.rust_crate_mapping among {}".format( |
| target_name, |
| fw_actions, |
| )) |
| expected_content = """grand_parent_proto |
| 2 |
| rust/test/rust_proto_library_unit_test/grandparent1.proto |
| rust/test/rust_proto_library_unit_test/grandparent2.proto |
| parent_proto |
| 1 |
| rust/test/rust_proto_library_unit_test/parent.proto |
| parent2_proto |
| 1 |
| rust/test/rust_proto_library_unit_test/parent2.proto |
| """ |
| if crate_mapping_action.content != expected_content: |
| fail("The crate mapping file content didn't match. Was: {}".format( |
| crate_mapping_action.content, |
| )) |
| |
| protoc_action = [ |
| a |
| for a in actions |
| for i in a.inputs.to_list() |
| if "rust_crate_mapping" in i.basename |
| ] |
| if not protoc_action: |
| fail("Couldn't find action with the rust_crate_mapping as input") |
| if protoc_action[0].mnemonic != "GenProto": |
| fail( |
| "Action that had rust_crate_mapping as input wasn't a GenProto action, but {}", |
| protoc_action[0].mnemonic, |
| ) |
| |
| def _find_rust_lib_input(inputs, target_name): |
| inputs = inputs.to_list() |
| input = [i for i in inputs if i.basename.startswith("lib" + target_name) and |
| (i.basename.endswith(".rlib") or i.basename.endswith(".rmeta"))] |
| if not input: |
| fail("Couldn't find lib{}-<hash>.rlib or lib{}-<hash>.rmeta among {}".format( |
| target_name, |
| target_name, |
| [i.basename for i in inputs], |
| )) |
| return input[0] |
| |
| def _relevant_linker_inputs(ltl): |
| return ltl.objects + ltl.pic_objects + ( |
| [ltl.static_library] if ltl.static_library else [] |
| ) + ( |
| [ltl.pic_static_library] if ltl.pic_static_library else [] |
| ) |
| |
| def _find_linker_input(rust_proto_info, basename_substring): |
| cc_info = rust_proto_info.dep_variant_info.cc_info |
| for linker_input in cc_info.linking_context.linker_inputs.to_list(): |
| for ltl in linker_input.libraries: |
| for file in _relevant_linker_inputs(ltl): |
| if basename_substring in file.basename: |
| return file |
| |
| fail("Couldn't find file with suffix {} in {}".format( |
| basename_substring, |
| [ |
| f.basename |
| for input in cc_info.linking_context.linker_inputs.to_list() |
| for ltl in input.libraries |
| for f in _relevant_linker_inputs(ltl) |
| ], |
| )) |
| |
| #################################################################################################### |
| |
| def _rust_upb_aspect_test_impl(ctx): |
| env = analysistest.begin(ctx) |
| target_under_test = analysistest.target_under_test(env) |
| actions = target_under_test[ActionsInfo].actions |
| rustc_action = _find_actions_with_mnemonic(actions, "Rustc")[0] |
| |
| # The protoc action needs to be given the crate mapping file |
| _check_crate_mapping(actions, "child_proto") |
| |
| # The action needs to have the Rust runtime as an input |
| _find_rust_lib_input(rustc_action.inputs, "protobuf") |
| |
| # The action needs to have rlibs for direct deps |
| _find_rust_lib_input(rustc_action.inputs, "parent") |
| _find_rust_lib_input(rustc_action.inputs, "parent2") |
| |
| # The action needs to produce a .rlib artifact (sometimes .rmeta as well, not tested here). |
| asserts.true(env, rustc_action.outputs.to_list()[0].path.endswith(".rlib")) |
| |
| # The aspect needs to provide CcInfo that passes UPB gencode to the eventual linking. |
| _find_linker_input(target_under_test[RustProtoInfo], "child.upb.thunks") |
| _find_linker_input(target_under_test[RustProtoInfo], "parent.upb.thunks") |
| |
| return analysistest.end(env) |
| |
| rust_upb_aspect_test = analysistest.make(_rust_upb_aspect_test_impl) |
| |
| def _test_upb_aspect(): |
| attach_upb_aspect(name = "child_proto_with_upb_aspect", dep = ":child_proto") |
| |
| rust_upb_aspect_test( |
| name = "rust_upb_aspect_test", |
| target_under_test = ":child_proto_with_upb_aspect", |
| ) |
| |
| #################################################################################################### |
| def _rust_cc_aspect_test_impl(ctx): |
| env = analysistest.begin(ctx) |
| target_under_test = analysistest.target_under_test(env) |
| actions = target_under_test[ActionsInfo].actions |
| rustc_action = _find_actions_with_mnemonic(actions, "Rustc")[0] |
| |
| _check_crate_mapping(actions, "child_proto") |
| |
| # The rustc action needs to have the Rust runtime as an input |
| _find_rust_lib_input(rustc_action.inputs, "protobuf") |
| |
| # The action needs to have rlibs for direct deps |
| _find_rust_lib_input(rustc_action.inputs, "parent") |
| _find_rust_lib_input(rustc_action.inputs, "parent2") |
| |
| # The action needs to produce a .rlib artifact (sometimes .rmeta as well, not tested here). |
| asserts.true(env, rustc_action.outputs.to_list()[0].path.endswith(".rlib")) |
| |
| # The aspect needs to provide CcInfo that passes UPB gencode to the eventual linking. |
| _find_linker_input(target_under_test[RustProtoInfo], "child.pb") |
| _find_linker_input(target_under_test[RustProtoInfo], "parent.pb") |
| |
| return analysistest.end(env) |
| |
| rust_cc_aspect_test = analysistest.make(_rust_cc_aspect_test_impl) |
| |
| def _test_cc_aspect(): |
| attach_cc_aspect(name = "child_proto_with_cc_aspect", dep = ":child_proto") |
| |
| rust_cc_aspect_test( |
| name = "rust_cc_aspect_test", |
| target_under_test = ":child_proto_with_cc_aspect", |
| ) |
| |
| #################################################################################################### |
| |
| def _rust_outputs_test_impl(ctx): |
| env = analysistest.begin(ctx) |
| target_under_test = analysistest.target_under_test(env) |
| |
| label_to_file = { |
| "child_rust_cc_proto": "child.c.pb.rs", |
| "child_rust_upb_proto": "child.u.pb.rs", |
| } |
| expected_output = label_to_file[target_under_test.label.name] |
| asserts.true(env, target_under_test.files.to_list()[0].path.endswith(expected_output)) |
| |
| return analysistest.end(env) |
| |
| rust_outputs_test = analysistest.make(_rust_outputs_test_impl) |
| |
| def _test_cc_outputs(): |
| rust_cc_proto_library( |
| name = "child_rust_cc_proto", |
| deps = [":child_proto"], |
| ) |
| |
| rust_outputs_test( |
| name = "rust_cc_outputs_test", |
| target_under_test = ":child_rust_cc_proto", |
| ) |
| |
| def _test_upb_outputs(): |
| rust_upb_proto_library( |
| name = "child_rust_upb_proto", |
| deps = [":child_proto"], |
| ) |
| |
| rust_outputs_test( |
| name = "rust_upb_outputs_test", |
| target_under_test = ":child_rust_upb_proto", |
| ) |
| |
| def rust_proto_library_unit_test(name): |
| """Sets up rust_proto_library_unit_test test suite. |
| |
| Args: |
| name: name of the test suite""" |
| native.proto_library( |
| # Use a '-' in the target name to test that its replaced by a '_' in the crate name. |
| name = "grand-parent_proto", |
| srcs = ["grandparent1.proto", "grandparent2.proto"], |
| ) |
| |
| native.proto_library( |
| name = "parent_proto", |
| srcs = ["parent.proto"], |
| deps = [":grand-parent_proto"], |
| ) |
| |
| native.proto_library(name = "parent2_proto", srcs = ["parent2.proto"]) |
| |
| native.proto_library( |
| name = "child_proto", |
| srcs = ["child.proto"], |
| deps = [":parent_proto", ":parent2_proto"], |
| ) |
| |
| _test_upb_aspect() |
| _test_cc_aspect() |
| _test_cc_outputs() |
| _test_upb_outputs() |
| |
| native.test_suite( |
| name = name, |
| tests = [ |
| ":rust_upb_aspect_test", |
| ":rust_cc_aspect_test", |
| ":rust_cc_outputs_test", |
| ":rust_upb_outputs_test", |
| ], |
| ) |