| """Unittests for rust rules.""" |
| |
| load("@bazel_skylib//lib:unittest.bzl", "analysistest", "asserts") |
| load("//rust:defs.bzl", "rust_binary", "rust_library", "rust_proc_macro") |
| load("//test/unit:common.bzl", "assert_argv_contains", "assert_list_contains_adjacent_elements", "assert_list_contains_adjacent_elements_not") |
| load(":wrap.bzl", "wrap") |
| |
| NOT_WINDOWS = select({ |
| "@platforms//os:linux": [], |
| "@platforms//os:macos": [], |
| "//conditions:default": ["@platforms//:incompatible"], |
| }) |
| |
| ENABLE_PIPELINING = { |
| "@//rust/settings:pipelined_compilation": True, |
| } |
| |
| def _second_lib_test_impl(ctx): |
| env = analysistest.begin(ctx) |
| tut = analysistest.target_under_test(env) |
| rlib_action = [act for act in tut.actions if act.mnemonic == "Rustc"][0] |
| metadata_action = [act for act in tut.actions if act.mnemonic == "RustcMetadata"][0] |
| |
| # Both actions should use the same --emit= |
| assert_argv_contains(env, rlib_action, "--emit=dep-info,link,metadata") |
| assert_argv_contains(env, metadata_action, "--emit=dep-info,link,metadata") |
| |
| # The metadata action should have a .rmeta as output and the rlib action a .rlib |
| path = rlib_action.outputs.to_list()[0].path |
| asserts.true( |
| env, |
| path.endswith(".rlib"), |
| "expected Rustc to output .rlib, got " + path, |
| ) |
| path = metadata_action.outputs.to_list()[0].path |
| asserts.true( |
| env, |
| path.endswith(".rmeta"), |
| "expected RustcMetadata to output .rmeta, got " + path, |
| ) |
| |
| # Only the action building metadata should contain --rustc-quit-on-rmeta |
| assert_list_contains_adjacent_elements_not(env, rlib_action.argv, ["--rustc-quit-on-rmeta", "true"]) |
| assert_list_contains_adjacent_elements(env, metadata_action.argv, ["--rustc-quit-on-rmeta", "true"]) |
| |
| # Check that both actions refer to the metadata of :first, not the rlib |
| extern_metadata = [arg for arg in metadata_action.argv if arg.startswith("--extern=first=") and "libfirst" in arg and arg.endswith(".rmeta")] |
| asserts.true( |
| env, |
| len(extern_metadata) == 1, |
| "did not find a --extern=first=*.rmeta but expected one", |
| ) |
| extern_rlib = [arg for arg in rlib_action.argv if arg.startswith("--extern=first=") and "libfirst" in arg and arg.endswith(".rmeta")] |
| asserts.true( |
| env, |
| len(extern_rlib) == 1, |
| "did not find a --extern=first=*.rlib but expected one", |
| ) |
| |
| # Check that the input to both actions is the metadata of :first |
| input_metadata = [i for i in metadata_action.inputs.to_list() if i.basename.startswith("libfirst")] |
| asserts.true(env, len(input_metadata) == 1, "expected only one libfirst input, found " + str([i.path for i in input_metadata])) |
| asserts.true(env, input_metadata[0].extension == "rmeta", "expected libfirst dependency to be rmeta, found " + input_metadata[0].path) |
| input_rlib = [i for i in rlib_action.inputs.to_list() if i.basename.startswith("libfirst")] |
| asserts.true(env, len(input_rlib) == 1, "expected only one libfirst input, found " + str([i.path for i in input_rlib])) |
| asserts.true(env, input_rlib[0].extension == "rmeta", "expected libfirst dependency to be rmeta, found " + input_rlib[0].path) |
| |
| return analysistest.end(env) |
| |
| def _bin_test_impl(ctx): |
| env = analysistest.begin(ctx) |
| tut = analysistest.target_under_test(env) |
| bin_action = [act for act in tut.actions if act.mnemonic == "Rustc"][0] |
| |
| # Check that no inputs to this binary are .rmeta files. |
| metadata_inputs = [i.path for i in bin_action.inputs.to_list() if i.path.endswith(".rmeta")] |
| asserts.false(env, metadata_inputs, "expected no metadata inputs, found " + str(metadata_inputs)) |
| |
| return analysistest.end(env) |
| |
| bin_test = analysistest.make(_bin_test_impl, config_settings = ENABLE_PIPELINING) |
| second_lib_test = analysistest.make(_second_lib_test_impl, config_settings = ENABLE_PIPELINING) |
| |
| def _pipelined_compilation_test(): |
| rust_proc_macro( |
| name = "my_macro", |
| edition = "2021", |
| srcs = ["my_macro.rs"], |
| ) |
| |
| rust_library( |
| name = "first", |
| edition = "2021", |
| srcs = ["first.rs"], |
| ) |
| |
| rust_library( |
| name = "second", |
| edition = "2021", |
| srcs = ["second.rs"], |
| deps = [":first"], |
| proc_macro_deps = [":my_macro"], |
| ) |
| |
| rust_binary( |
| name = "bin", |
| edition = "2021", |
| srcs = ["bin.rs"], |
| deps = [":second"], |
| ) |
| |
| second_lib_test(name = "second_lib_test", target_under_test = ":second", target_compatible_with = NOT_WINDOWS) |
| bin_test(name = "bin_test", target_under_test = ":bin", target_compatible_with = NOT_WINDOWS) |
| |
| def _rmeta_is_propagated_through_custom_rule_test_impl(ctx): |
| env = analysistest.begin(ctx) |
| tut = analysistest.target_under_test(env) |
| |
| # This is the metadata-generating action. It should depend on metadata for the library and, if generate_metadata is set |
| # also depend on metadata for 'wrapper'. |
| rust_action = [act for act in tut.actions if act.mnemonic == "RustcMetadata"][0] |
| |
| metadata_inputs = [i for i in rust_action.inputs.to_list() if i.path.endswith(".rmeta")] |
| rlib_inputs = [i for i in rust_action.inputs.to_list() if i.path.endswith(".rlib")] |
| |
| seen_wrapper_metadata = False |
| seen_to_wrap_metadata = False |
| for mi in metadata_inputs: |
| if "libwrapper" in mi.path: |
| seen_wrapper_metadata = True |
| if "libto_wrap" in mi.path: |
| seen_to_wrap_metadata = True |
| |
| seen_wrapper_rlib = False |
| seen_to_wrap_rlib = False |
| for ri in rlib_inputs: |
| if "libwrapper" in ri.path: |
| seen_wrapper_rlib = True |
| if "libto_wrap" in ri.path: |
| seen_to_wrap_rlib = True |
| |
| if ctx.attr.generate_metadata: |
| asserts.true(env, seen_wrapper_metadata, "expected dependency on metadata for 'wrapper' but not found") |
| asserts.false(env, seen_wrapper_rlib, "expected no dependency on object for 'wrapper' but it was found") |
| else: |
| asserts.true(env, seen_wrapper_rlib, "expected dependency on object for 'wrapper' but not found") |
| asserts.false(env, seen_wrapper_metadata, "expected no dependency on metadata for 'wrapper' but it was found") |
| |
| asserts.true(env, seen_to_wrap_metadata, "expected dependency on metadata for 'to_wrap' but not found") |
| asserts.false(env, seen_to_wrap_rlib, "expected no dependency on object for 'to_wrap' but it was found") |
| |
| return analysistest.end(env) |
| |
| def _rmeta_is_used_when_building_custom_rule_test_impl(ctx): |
| env = analysistest.begin(ctx) |
| tut = analysistest.target_under_test(env) |
| |
| # This is the custom rule invocation of rustc. |
| rust_action = [act for act in tut.actions if act.mnemonic == "Rustc"][0] |
| |
| # We want to check that the action depends on metadata, regardless of ctx.attr.generate_metadata |
| seen_to_wrap_rlib = False |
| seen_to_wrap_rmeta = False |
| for act in rust_action.inputs.to_list(): |
| if "libto_wrap" in act.path and act.path.endswith(".rlib"): |
| seen_to_wrap_rlib = True |
| elif "libto_wrap" in act.path and act.path.endswith(".rmeta"): |
| seen_to_wrap_rmeta = True |
| |
| asserts.true(env, seen_to_wrap_rmeta, "expected dependency on metadata for 'to_wrap' but not found") |
| asserts.false(env, seen_to_wrap_rlib, "expected no dependency on object for 'to_wrap' but it was found") |
| |
| return analysistest.end(env) |
| |
| rmeta_is_propagated_through_custom_rule_test = analysistest.make(_rmeta_is_propagated_through_custom_rule_test_impl, attrs = {"generate_metadata": attr.bool()}, config_settings = ENABLE_PIPELINING) |
| rmeta_is_used_when_building_custom_rule_test = analysistest.make(_rmeta_is_used_when_building_custom_rule_test_impl, config_settings = ENABLE_PIPELINING) |
| |
| def _rmeta_not_produced_if_pipelining_disabled_test_impl(ctx): |
| env = analysistest.begin(ctx) |
| tut = analysistest.target_under_test(env) |
| |
| rust_action = [act for act in tut.actions if act.mnemonic == "RustcMetadata"] |
| asserts.true(env, len(rust_action) == 0, "expected no metadata to be produced, but found a metadata action") |
| |
| return analysistest.end(env) |
| |
| rmeta_not_produced_if_pipelining_disabled_test = analysistest.make(_rmeta_not_produced_if_pipelining_disabled_test_impl, config_settings = ENABLE_PIPELINING) |
| |
| def _disable_pipelining_test(): |
| rust_library( |
| name = "lib", |
| srcs = ["custom_rule_test/to_wrap.rs"], |
| edition = "2021", |
| disable_pipelining = True, |
| ) |
| rmeta_not_produced_if_pipelining_disabled_test( |
| name = "rmeta_not_produced_if_pipelining_disabled_test", |
| target_compatible_with = NOT_WINDOWS, |
| target_under_test = ":lib", |
| ) |
| |
| def _custom_rule_test(generate_metadata, suffix): |
| rust_library( |
| name = "to_wrap" + suffix, |
| crate_name = "to_wrap", |
| srcs = ["custom_rule_test/to_wrap.rs"], |
| edition = "2021", |
| ) |
| wrap( |
| name = "wrapper" + suffix, |
| crate_name = "wrapper", |
| target = ":to_wrap" + suffix, |
| generate_metadata = generate_metadata, |
| ) |
| rust_library( |
| name = "uses_wrapper" + suffix, |
| srcs = ["custom_rule_test/uses_wrapper.rs"], |
| deps = [":wrapper" + suffix], |
| edition = "2021", |
| ) |
| |
| rmeta_is_propagated_through_custom_rule_test( |
| name = "rmeta_is_propagated_through_custom_rule_test" + suffix, |
| generate_metadata = generate_metadata, |
| target_compatible_with = NOT_WINDOWS, |
| target_under_test = ":uses_wrapper" + suffix, |
| ) |
| |
| rmeta_is_used_when_building_custom_rule_test( |
| name = "rmeta_is_used_when_building_custom_rule_test" + suffix, |
| target_compatible_with = NOT_WINDOWS, |
| target_under_test = ":wrapper" + suffix, |
| ) |
| |
| def pipelined_compilation_test_suite(name): |
| """Entry-point macro called from the BUILD file. |
| |
| Args: |
| name: Name of the macro. |
| """ |
| _pipelined_compilation_test() |
| _disable_pipelining_test() |
| _custom_rule_test(generate_metadata = True, suffix = "_with_metadata") |
| _custom_rule_test(generate_metadata = False, suffix = "_without_metadata") |
| |
| native.test_suite( |
| name = name, |
| tests = [ |
| ":bin_test", |
| ":second_lib_test", |
| ":rmeta_is_propagated_through_custom_rule_test_with_metadata", |
| ":rmeta_is_propagated_through_custom_rule_test_without_metadata", |
| ":rmeta_is_used_when_building_custom_rule_test_with_metadata", |
| ":rmeta_is_used_when_building_custom_rule_test_without_metadata", |
| ":rmeta_not_produced_if_pipelining_disabled_test", |
| ], |
| ) |