Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 28 additions & 9 deletions kotlin/internal/jvm/impl.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,10 @@ load(
)
load(
"//kotlin/internal/jvm:kover.bzl",
_create_kover_agent_actions = "create_kover_agent_actions",
_create_kover_metadata_action = "create_kover_metadata_action",
_get_kover_agent_files = "get_kover_agent_file",
_get_kover_jvm_flags = "get_kover_jvm_flags",
_is_kover_enabled = "is_kover_enabled",
_kover_jvm_flags_setup = "kover_jvm_flags_setup",
)
load(
"//kotlin/internal/utils:utils.bzl",
Expand Down Expand Up @@ -124,6 +123,7 @@ def _write_launcher_action(ctx, rjars, main_class, jvm_flags):
"%set_java_coverage_new_implementation%": """export JAVA_COVERAGE_NEW_IMPLEMENTATION=YES""",
"%test_runtime_classpath_file%": "export TEST_RUNTIME_CLASSPATH_FILE=${JAVA_RUNFILES}",
"%workspace_prefix%": ctx.workspace_name + "/",
"%kover_jvm_flags_setup%": "",
},
is_executable = True,
)
Expand All @@ -133,6 +133,30 @@ def _write_launcher_action(ctx, rjars, main_class, jvm_flags):
["${RUNPATH}%s" % (j.short_path) for j in rjars.to_list()],
)

if ctx.configuration.coverage_enabled and _is_kover_enabled(ctx):
kover_agent_files = _get_kover_agent_files(ctx)
ctx.actions.expand_template(
template = template,
output = ctx.outputs.executable,
substitutions = {
"%classpath%": classpath,
"%java_start_class%": main_class,
"%javabin%": java_bin,
"%jvm_flags%": jvm_flags,
"%needs_runfiles%": "0" if _is_absolute(java_bin_path) else "1",
"%runfiles_manifest_only%": "",
"%set_jacoco_java_runfiles_root%": "",
"%set_jacoco_main_class%": "",
"%set_jacoco_metadata%": "",
"%set_java_coverage_new_implementation%": """export JAVA_COVERAGE_NEW_IMPLEMENTATION=NO""",
"%test_runtime_classpath_file%": "export TEST_RUNTIME_CLASSPATH_FILE=${JAVA_RUNFILES}",
"%workspace_prefix%": ctx.workspace_name + "/",
"%kover_jvm_flags_setup%": _kover_jvm_flags_setup(kover_agent_files, ctx.label.package, ctx.attr.name),
},
is_executable = True,
)
return []

ctx.actions.expand_template(
template = template,
output = ctx.outputs.executable,
Expand All @@ -149,6 +173,7 @@ def _write_launcher_action(ctx, rjars, main_class, jvm_flags):
"%set_java_coverage_new_implementation%": """export JAVA_COVERAGE_NEW_IMPLEMENTATION=NO""",
"%test_runtime_classpath_file%": "export TEST_RUNTIME_CLASSPATH_FILE=${JAVA_RUNFILES}",
"%workspace_prefix%": ctx.workspace_name + "/",
"%kover_jvm_flags_setup%": "",
},
is_executable = True,
)
Expand Down Expand Up @@ -299,19 +324,13 @@ def kt_jvm_junit_test_impl(ctx):
if ctx.configuration.coverage_enabled:
if _is_kover_enabled(ctx):
kover_agent_files = _get_kover_agent_files(ctx)
kover_output_file, kover_args_file = _create_kover_agent_actions(ctx, ctx.attr.name)
kover_output_metadata_file = _create_kover_metadata_action(
ctx,
ctx.attr.name,
ctx.attr.deps + ctx.attr.associates,
kover_output_file,
)
flags = _get_kover_jvm_flags(kover_agent_files, kover_args_file)

# add Kover agent jvm_flag, inputs and outputs
coverage_jvm_flags = [flags]
coverage_inputs = [depset(kover_agent_files)]
coverage_runfiles = [kover_args_file, kover_output_metadata_file]
coverage_runfiles = [kover_output_metadata_file]
else:
jacocorunner = ctx.toolchains[_TOOLCHAIN_TYPE].jacocorunner
coverage_runfiles = jacocorunner.files.to_list()
Expand Down
61 changes: 4 additions & 57 deletions kotlin/internal/jvm/kover.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -75,61 +75,11 @@ def get_kover_agent_file(ctx):
kover_agent_info = kover_agent[DefaultInfo]
return kover_agent_info.files.to_list()

def get_kover_jvm_flags(kover_agent_files, kover_args_file):
"""Compute the jvm flags used to setup Kover agent.
def kover_jvm_flags_setup(kover_agent_files, pkg, name):
"""Return the %kover_jvm_flags_setup% substitution — a call to _setup_kover in the template."""
return '_setup_kover "%s" "%s" "%s"' % (kover_agent_files[0].short_path, pkg, name)

Args:
kover_agent_files: List of Kover agent files.
kover_args_file: The Kover arguments file.

Returns:
The flag string to be used by test runner JVM.
"""
jvm_args = [
"-Xbootclasspath/a:%s" % (kover_agent_files[0].short_path),
"-javaagent:%s=file:%s" % (kover_agent_files[0].short_path, kover_args_file.short_path),
]
return " ".join(jvm_args)

def create_kover_agent_actions(ctx, name):
"""Generate the actions needed to emit Kover code coverage metadata file.

Creates the properly populated arguments input file needed by Kover agent.

Args:
ctx: The rule context.
name: The name of the target.

Returns:
A tuple of (kover_output_file, kover_args_file).
"""

# declare code coverage raw data binary output file
binary_output_name = "%s-kover_report.ic" % name
kover_output_file = ctx.actions.declare_file(binary_output_name)

# Hack: there is curently no way to indicate this file will be created Kover agent
ctx.actions.run_shell(
outputs = [kover_output_file],
command = "touch {}".format(kover_output_file.path),
)

# declare args file - https://kotlin.github.io/kotlinx-kover/jvm-agent/#kover-jvm-arguments-file
kover_args_file = ctx.actions.declare_file(
"%s-kover.args.txt" % name,
)
ctx.actions.write(
kover_args_file,
"report.file=../../%s" % binary_output_name, # Kotlin compiler runs in runfiles folder, make sure file is created is correct location
)

return kover_output_file, kover_args_file

def create_kover_metadata_action(
ctx,
name,
deps,
kover_output_file):
def create_kover_metadata_action(ctx, name, deps):
"""Generate kover metadata file needed for invoking kover CLI to generate report.

More info at: https://kotlin.github.io/kotlinx-kover/cli/
Expand All @@ -138,7 +88,6 @@ def create_kover_metadata_action(
ctx: The rule context.
name: The name of the target.
deps: The dependencies to collect coverage for.
kover_output_file: The Kover output file.

Returns:
The kover output metadata file.
Expand Down Expand Up @@ -177,8 +126,6 @@ def create_kover_metadata_action(
excludes.extend(["--excludeInheritedFrom", exclude_inherited_from])

ctx.actions.write(kover_output_metadata_file, "\n".join([
"report",
kover_output_file.path,
"--title",
"Code-Coverage Analysis: %s" % ctx.label,
] + srcs + classfiles + excludes))
Expand Down
18 changes: 9 additions & 9 deletions kotlin/internal/jvm/kt_android_local_test_impl.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -76,11 +76,10 @@ load(
)
load(
"//kotlin/internal/jvm:kover.bzl",
_create_kover_agent_actions = "create_kover_agent_actions",
_create_kover_metadata_action = "create_kover_metadata_action",
_get_kover_agent_files = "get_kover_agent_file",
_get_kover_jvm_flags = "get_kover_jvm_flags",
_is_kover_enabled = "is_kover_enabled",
_kover_jvm_flags_setup = "kover_jvm_flags_setup",
)

_JACOCOCO_CLASS = "com.google.testing.coverage.JacocoCoverageRunner"
Expand Down Expand Up @@ -142,18 +141,13 @@ def _process_jvm(ctx, resources_ctx, **_unused_sub_ctxs):
if ctx.configuration.coverage_enabled:
if _is_kover_enabled(ctx):
kover_agent_files = _get_kover_agent_files(ctx)
kover_output_file, kover_args_file = _create_kover_agent_actions(ctx, ctx.attr.name)
kover_output_metadata_file = _create_kover_metadata_action(
ctx,
ctx.attr.name,
ctx.attr.deps + ctx.attr.associates,
kover_output_file,
)

flags = _get_kover_jvm_flags(kover_agent_files, kover_args_file)
jvm_flags.append(flags)

transitive.extend([depset(kover_agent_files), depset([kover_args_file]), depset([kover_output_metadata_file])])
transitive.extend([depset(kover_agent_files), depset([kover_output_metadata_file])])

java_start_class = ctx.attr.main_class
coverage_start_class = None
Expand Down Expand Up @@ -336,10 +330,16 @@ def _create_stub(
subs["%set_jacoco_main_class%"] = ""
subs["%set_jacoco_java_runfiles_root%"] = ""

if ctx.configuration.coverage_enabled and _is_kover_enabled(ctx):
kover_agent_files = _get_kover_agent_files(ctx)
subs["%kover_jvm_flags_setup%"] = _kover_jvm_flags_setup(kover_agent_files, ctx.label.package, ctx.attr.name)
else:
subs["%kover_jvm_flags_setup%"] = ""

subs.update(substitutes)

ctx.actions.expand_template(
template = _utils.only(_get_android_toolchain(ctx).java_stub.files.to_list()),
template = ctx.attr.java_stub_template.files.to_list()[0],

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This need confirmation that we do not have any extra modifications for rules_android in stub file

output = stub_file,
substitutions = subs,
is_executable = True,
Expand Down
40 changes: 18 additions & 22 deletions src/test/starlark/internal/jvm/kover_test.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -15,45 +15,41 @@
"""Tests for Kover code coverage integration."""

load("@bazel_skylib//lib:unittest.bzl", "asserts", "unittest")
load("//kotlin/internal/jvm:kover.bzl", "get_kover_jvm_flags")
load("//kotlin/internal/jvm:kover.bzl", "kover_jvm_flags_setup")

def _get_kover_jvm_flags_test_impl(ctx):
"""Test that get_kover_jvm_flags generates correct JVM agent flags including bootclasspath."""
def _kover_jvm_flags_setup_test_impl(ctx):
"""Test that kover_jvm_flags_setup emits the _setup_kover shell call with agent path, package and name."""
env = unittest.begin(ctx)

# Create mock file objects with short_path attribute
# Create a mock agent file object with a short_path attribute.
mock_agent_file = struct(short_path = "external/kover/kover-jvm-agent.jar")
mock_args_file = struct(short_path = "bazel-out/k8-fastbuild/bin/test-kover.args.txt")

result = get_kover_jvm_flags([mock_agent_file], mock_args_file)
result = kover_jvm_flags_setup([mock_agent_file], "path/to/pkg", "my_test")

# Expected format includes both -Xbootclasspath/a and -javaagent flags
expected = "-Xbootclasspath/a:external/kover/kover-jvm-agent.jar -javaagent:external/kover/kover-jvm-agent.jar=file:bazel-out/k8-fastbuild/bin/test-kover.args.txt"
expected = '_setup_kover "external/kover/kover-jvm-agent.jar" "path/to/pkg" "my_test"'
asserts.equals(env, expected, result)

return unittest.end(env)

get_kover_jvm_flags_test = unittest.make(_get_kover_jvm_flags_test_impl)
_kover_jvm_flags_setup_test = unittest.make(_kover_jvm_flags_setup_test_impl)

def _kover_jvm_flags_format_test_impl(ctx):
"""Test JVM flags format with different path patterns."""
def _kover_jvm_flags_setup_format_test_impl(ctx):
"""Test the kover_jvm_flags_setup call format with different path patterns."""
env = unittest.begin(ctx)

# Test with workspace-relative path
mock_agent = struct(short_path = "maven/kover-agent-1.0.jar")
mock_args = struct(short_path = "pkg/test.args")

result = get_kover_jvm_flags([mock_agent], mock_args)
result = kover_jvm_flags_setup([mock_agent], "pkg", "test")

# Verify the format includes both bootclasspath and javaagent flags
asserts.true(env, "-Xbootclasspath/a:" in result)
asserts.true(env, "-javaagent:" in result)
asserts.true(env, "=file:" in result)
asserts.true(env, result.endswith("pkg/test.args"))
# Verify the call uses the first agent file and quotes all three arguments.
asserts.true(env, result.startswith("_setup_kover "))
asserts.true(env, '"maven/kover-agent-1.0.jar"' in result)
asserts.true(env, '"pkg"' in result)
asserts.true(env, result.endswith('"test"'))

return unittest.end(env)

kover_jvm_flags_format_test = unittest.make(_kover_jvm_flags_format_test_impl)
_kover_jvm_flags_setup_format_test = unittest.make(_kover_jvm_flags_setup_format_test_impl)

def kover_test_suite(name):
"""Create the test suite for Kover integration tests.
Expand All @@ -63,6 +59,6 @@ def kover_test_suite(name):
"""
unittest.suite(
name,
get_kover_jvm_flags_test,
kover_jvm_flags_format_test,
_kover_jvm_flags_setup_test,
_kover_jvm_flags_setup_format_test,
)
11 changes: 11 additions & 0 deletions third_party/java_stub_template.txt
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,17 @@ if [[ -n "$TEST_TMPDIR" && -d "$TEST_TMPDIR" ]]; then
JVM_FLAGS+=" -Djava.io.tmpdir=$TEST_TMPDIR"
fi

function _setup_kover() {
local agent="$1" pkg="$2" name="$3"
local ic
ic=$(mktemp)
mkdir -p "${TEST_UNDECLARED_OUTPUTS_DIR}/${pkg}"
echo "report.file=${TEST_UNDECLARED_OUTPUTS_DIR}/${pkg}/${name}-kover_report.ic" > "$ic"
JVM_FLAGS="${JVM_FLAGS} -Xbootclasspath/a:${agent} -javaagent:${agent}=file:${ic}"
}

%kover_jvm_flags_setup%

ARGS=(
${JVM_DEBUG_FLAGS}
${JVM_FLAGS}
Expand Down