forked from bazelbuild/rules_kotlin
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This diff contains logic to integrate with Kover for code coverage, using Kover JVM agent and disabling JaCoCo instrumentation, which avoid having to re-compile application code. It used from both JVM and Android kotlin tests. How to use? Supply the version of Kover agent via toolchain (typically from jvm_rules_extrenal), and enable Kover (separate diff). Then run bazel coverage //your/kotlin/test_target. Output files are created in working module directory (along test/library explicity outputs). Please note : Notes : Because Bazel test/coverage are 'terminal' and actions or aspects can't reuse the output of these, the generation of the report is done outside bazel (typically from Bazel wrapper). The logic here will generate both raw output (*.ic file) and a metadata file ready to provide to Kover CLI, so that one can generate report simply by running : java -jar kover-cli.jar report @path_to_metadat_file <options> We could possibly generate the report by hijacking test runner shell script template and injecting this command to executed after tests are run. This is rather hacky and is likely to require changes to Bazel project. For mixed sourceset, disabling JaCoCo instrumenation is required. To do this properly, one should add an extra parameter to java_common.compile() API, which require modifying both rules_java and Bazel core. For now, we disabled JaCoCo instrumentation accross the board, you will need to cherry-pick this PR uber-common/bazel@cb9f6f0 Code in kt_android_local_test_impl.bzl needs to be kept in sync with rules_android. There is ongoing conversation with google to simply of to extend rules_android, and override pipeline's behavior without duplicating their code, we should be able to simplify this soon. fix typo
- Loading branch information
1 parent
03c3521
commit a92d8e6
Showing
5 changed files
with
438 additions
and
11 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,179 @@ | ||
# Copyright 2018 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. | ||
|
||
# This file contains logic to integrate with Kover for code coverage, using | ||
# Kover JVM agent and disabling JaCoCo instrumentation, which avoid having to | ||
# re-compile application code. It used from both JVM and Android kotlin tests. | ||
# | ||
# | ||
# How to use? | ||
# | ||
# Supply the version of Kover agent via toolchain (typically from jvm_rules_extrenal), | ||
# and enable Kover. Then run `bazel coverage //your/kotlin/test_target`. Output files | ||
# are created in working module directory (along test/library explicity outputs). | ||
# | ||
# | ||
# Notes : | ||
# | ||
# 1. Because Bazel test/coverage are 'terminal' and actions or aspects can't reuse the output | ||
# of these, the generation of the report is done outside bazel (typically | ||
# from Bazel wrapper). The logic here will generate both raw output (*.ic file) and | ||
# a metadata file ready to provide to Kover CLI, so that one can generate report simply by | ||
# running : `java -jar kover-cli.jar report @path_to_metadat_file <options>` | ||
# | ||
# We could possibly generate the report by hijacking test runner shell script template | ||
# and injecting this command to executed after tests are run. This is rather hacky | ||
# and is likely to require changes to Bazel project. | ||
# | ||
# 2. For mixed sourceset, disabling JaCoCo instrumenation is required. To do this properly, | ||
# one should add an extra parameter to java_common.compile() API, which require modifying both | ||
# rules_java and Bazel core. For now, we disabled JaCoCo instrumentation accross the board, | ||
# you will need to cherry-pick this PR https://github.com/uber-common/bazel/commit/cb9f6f042c64af96bbd77e21fe6fb75936c74f47 | ||
# | ||
# 3. Code in `kt_android_local_test_impl.bzl` needs to be kept in sync with rules_android. There is ongoing | ||
# conversation with google to simply of to extend rules_android, and override pipeline's behavior without | ||
# duplicating their code, we should be able to simplify this soon. | ||
# | ||
|
||
load( | ||
"//kotlin/internal:defs.bzl", | ||
_KtJvmInfo = "KtJvmInfo", | ||
_TOOLCHAIN_TYPE = "TOOLCHAIN_TYPE", | ||
) | ||
load("@bazel_skylib//lib:paths.bzl", | ||
_paths = "paths", | ||
) | ||
|
||
def is_kover_enabled(ctx): | ||
return ctx.toolchains[_TOOLCHAIN_TYPE].experimental_kover_enabled | ||
|
||
def get_kover_agent_file(ctx): | ||
""" Get the Kover agent runtime files, extracted from toolchain. | ||
returns: | ||
the Kover agent runtime files | ||
""" | ||
|
||
kover_agent = ctx.toolchains[_TOOLCHAIN_TYPE].experimental_kover_agent | ||
if not kover_agent: | ||
fail("Kover agent wasn't specified in toolchain.") | ||
|
||
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 flag used to setup Kover agent. | ||
returns: | ||
the flag string to be used by test runner jvm | ||
""" | ||
|
||
return "-javaagent:%s=%s" % (kover_agent_files[0].short_path, kover_args_file.short_path) | ||
|
||
def create_kover_agent_actions(ctx, name): | ||
""" Generate the actions needed to emit Kover code coverage metadata file. It creates | ||
the properly populated arguments input file needed by Kover agent. | ||
returns: | ||
the kover metadata output file. | ||
the kover arguments 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, | ||
) | ||
|
||
# The format specified in official documentation is failing, we follow instead the format | ||
# described in the provided error output (see below). | ||
# | ||
# Failed to parse agent arguments: java.lang.IllegalArgumentException: At least 5 arguments expected but 1 found. | ||
# Expected arguments are: | ||
# 0) data file to save coverage result | ||
# 1) a flag to enable tracking per test coverage | ||
# 2) a flag to calculate coverage for unloaded classes | ||
# 3) a flag to use data file as initial coverage, also use it if several parallel processes are to write into one file | ||
# 4) a flag to run line coverage or branch coverage otherwise | ||
|
||
ctx.actions.write(kover_args_file, "\n".join([ | ||
"../../%s" % binary_output_name, # Kotlin compiler runs in runfiles folder, make sure file is created is correct location | ||
"true", | ||
"false", | ||
"true", | ||
"true" | ||
])) | ||
|
||
return kover_output_file, kover_args_file | ||
|
||
|
||
def create_kover_metadata_action( | ||
ctx, | ||
name, | ||
deps, | ||
kover_output_file): | ||
""" Generate kover metadata file needed for invoking kover CLI to generate report. | ||
More info at: https://kotlin.github.io/kotlinx-kover/cli/ | ||
returns: | ||
the kover output metadata file. | ||
""" | ||
|
||
metadata_output_name = "%s-kover_metadata.txt" % name | ||
kover_output_metadata_file = ctx.actions.declare_file(metadata_output_name) | ||
|
||
srcs = [] | ||
classfiles = [] | ||
excludes = [] | ||
|
||
for dep in deps: | ||
if dep.label.package != ctx.label.package: | ||
continue | ||
|
||
if InstrumentedFilesInfo in dep: | ||
for src in dep[InstrumentedFilesInfo].instrumented_files.to_list(): | ||
if src.short_path.startswith(ctx.label.package + "/"): | ||
path = _paths.dirname(src.short_path) | ||
if path not in srcs: | ||
srcs.extend(["--src", path]) | ||
|
||
if JavaInfo in dep: | ||
for classfile in dep[JavaInfo].transitive_runtime_jars.to_list(): | ||
if classfile.short_path.startswith(ctx.label.package + "/"): | ||
if classfile.path not in classfiles: | ||
classfiles.extend(["--classfiles", classfile.path]) | ||
|
||
for exclude in ctx.toolchains[_TOOLCHAIN_TYPE].experimental_kover_exclude: | ||
excludes.extend(["--exclude", exclude]) | ||
|
||
for exclude_annotation in ctx.toolchains[_TOOLCHAIN_TYPE].experimental_kover_exclude_annotation: | ||
excludes.extend(["--excludeAnnotation", exclude_annotation]) | ||
|
||
ctx.actions.write(kover_output_metadata_file, "\n".join([ | ||
"report", | ||
kover_output_file.path, | ||
"--title", | ||
"Code-Coverage Analysis: %s" % ctx.label, | ||
] + srcs + classfiles + excludes)) | ||
|
||
return kover_output_metadata_file |
Oops, something went wrong.