Add a test for `java_binary` native library paths with transitive deps.

(ignore-relnotes)

PiperOrigin-RevId: 954607707
Change-Id: I163f3820d33bc0f2c0b34af2b66dc9ffc365b96b
diff --git a/test/java/common/rules/common_launcher_java_binary_tests.bzl b/test/java/common/rules/common_launcher_java_binary_tests.bzl
index 56b49d3..b2cc206 100644
--- a/test/java/common/rules/common_launcher_java_binary_tests.bzl
+++ b/test/java/common/rules/common_launcher_java_binary_tests.bzl
@@ -1,10 +1,13 @@
 """Parameterized tests for java_binary with --java_launcher"""
 
+load("@rules_cc//cc:cc_binary.bzl", "cc_binary")
+load("@rules_cc//cc:cc_library.bzl", "cc_library")
 load("@rules_testing//lib:analysis_test.bzl", "analysis_test", "test_suite")
 load("@rules_testing//lib:util.bzl", "util")
 load("//java:java_binary.bzl", "java_binary")
 load("//java:java_library.bzl", "java_library")
 load("//java:java_test.bzl", "java_test")
+load("//java/common/rules:java_helper.bzl", "helper")
 load("//test/java/testutil:artifact_closure.bzl", "artifact_closure")
 load("//test/java/testutil:binary_executable_subject.bzl", "expect_that_executable")
 load("//test/java/testutil:mock_java_toolchain.bzl", "mock_java_toolchain")
@@ -217,6 +220,58 @@
 def _test_java_test_has_assertions_enabled_impl(env, target):
     expect_that_executable.of_target(env, target).jvm_flags().contains("-ea")
 
+# Tests that the native library path computed from a ".so" cc_binary dependency
+# includes the transitive closure of that dependency.
+def _test_java_binary_native_library_path_includes_transitive_deps(name):
+    util.helper_target(
+        java_binary,
+        name = name + "/app",
+        srcs = ["DoesNotMatter.java"],
+        deps = [name + "/jni.so"],
+    )
+    util.helper_target(
+        java_binary,
+        name = name + "/runtime_app",
+        srcs = ["AlsoDoesNotMatter.java"],
+        runtime_deps = [name + "/jni.so"],
+    )
+    util.helper_target(
+        cc_binary,
+        name = name + "/jni.so",
+        srcs = ["lib1.so"],
+        deps = [name + "/helper_lib"],
+    )
+    util.helper_target(
+        cc_library,
+        name = name + "/helper_lib",
+        srcs = ["lib2.so"],
+    )
+
+    analysis_test(
+        name = name,
+        attrs = {
+            "_windows_constraints": attr.label_list(default = ["@platforms//os:windows"]),
+            "_cc_toolchain": attr.label(default = Label("@bazel_tools//tools/cpp:current_cc_toolchain")),
+        },
+        impl = _test_java_binary_native_library_path_includes_transitive_deps_impl,
+        target = [
+            name + "/app",
+            name + "/runtime_app",
+        ],
+    )
+
+def _test_java_binary_native_library_path_includes_transitive_deps_impl(env, targets):
+    for target in targets:
+        if helper.is_target_platform_windows(env.ctx):
+            expect_that_executable.of_target(env, target).native_library_paths().contains_exactly([
+                "${{JAVA_RUNFILES}}/{workspace}/{package}",
+            ])
+        else:
+            expect_that_executable.of_target(env, target).native_library_paths().contains_exactly([
+                "${{JAVA_RUNFILES}}/{workspace}/_solib_{cpu}/_//{package}:{test_name}/helper_lib___{package}",
+                "${{JAVA_RUNFILES}}/{workspace}/_solib_{cpu}/_//{package}:{test_name}/jni.so___{package}",
+            ])
+
 def java_binary_launcher_tests(name):
     test_suite(
         name = name,
@@ -230,5 +285,6 @@
             _test_java_test_main_class,
             _test_java_test_main_class_with_dot,
             _test_java_test_has_assertions_enabled,
+            _test_java_binary_native_library_path_includes_transitive_deps,
         ],
     )
diff --git a/test/java/testutil/binary_executable_subject.bzl b/test/java/testutil/binary_executable_subject.bzl
index 8e615a0..df5c477 100644
--- a/test/java/testutil/binary_executable_subject.bzl
+++ b/test/java/testutil/binary_executable_subject.bzl
@@ -1,5 +1,6 @@
 """Custom rules_testing subject for java_binary/java_test executable output"""
 
+load("@rules_cc//cc/common:cc_common.bzl", "cc_common")
 load("@rules_testing//lib:truth.bzl", "subjects")
 load("//java/common:java_semantics.bzl", "semantics")
 
@@ -10,6 +11,7 @@
         java_start_class = lambda: _java_start_class_subject(action_subject),
         test_suite = lambda: _test_suite_subject(action_subject),
         jvm_flags = lambda: _jvm_flags_subject(action_subject),
+        native_library_paths = lambda: _native_library_paths_subject(action_subject),
     )
     return public
 
@@ -42,6 +44,22 @@
             desc = "jvm_flags",
         ).offset(0, factory = subjects.str).split("\t")
 
+def _native_library_paths_subject(action):
+    return _jvm_flags_subject(action).transform(
+        filter = lambda e: e.startswith("-Djava.library.path="),
+        map_each = lambda e: e.split("=", 1)[1],
+        desc = "native library paths",
+    ).offset(
+        0,
+        factory = lambda actual, meta: subjects.str(actual, meta.derive(
+            format_str_kwargs = {"cpu": meta.ctx.attr._cc_toolchain[cc_common.CcToolchainInfo].cpu},
+        )),
+    ).split(":").transform(
+        map_each = lambda e: e.replace("_U", "_").replace("_S", "/").replace("_C", ":"),
+        desc = "pretty",
+        format = True,
+    )
+
 expect_that_executable = struct(
     of_target = _of_target,
 )