add support for code coverage
diff --git a/kotlin/builder/BUILD b/kotlin/builder/BUILD index 7a104a5..0c8703b 100644 --- a/kotlin/builder/BUILD +++ b/kotlin/builder/BUILD
@@ -21,6 +21,12 @@ "@com_github_jetbrains_kotlin//:kotlin-script-runtime", ] +ASM_DEPS = [ + "@io_bazel_rules_kotlin_org_ow2_asm_asm_commons//jar", + "@io_bazel_rules_kotlin_org_ow2_asm_asm//jar", + "@io_bazel_rules_kotlin_org_ow2_asm_asm_tree//jar", +] + # Common depset for the builder. COMMON_DEPS = [ "//kotlin/builder/proto:deps", @@ -38,6 +44,7 @@ "@com_github_jetbrains_kotlin//:kotlin-stdlib", "@com_github_jetbrains_kotlin//:kotlin-stdlib-jdk7", "@com_github_jetbrains_kotlin//:kotlin-stdlib-jdk8", + "@io_bazel_rules_kotlin_org_jacoco_org_jacoco_core//jar", ] # The compiler library, this is co-located in the kotlin compiler classloader. @@ -73,8 +80,9 @@ name = "builder", main_class = "io.bazel.kotlin.builder.KotlinBuilder", visibility = ["//visibility:public"], - runtime_deps = [ + runtime_deps = ASM_DEPS + [ ":builder_lib", + "@com_github_jetbrains_kotlin//:kotlin-reflect", ], data = [ ":compiler_lib.jar" @@ -115,6 +123,7 @@ "//third_party/jvm/com/google/truth", "//third_party/jvm/junit", ], + runtime_deps = ASM_DEPS ) java_test(
diff --git a/kotlin/builder/integrationtests/KotlinBuilderActionTests.java b/kotlin/builder/integrationtests/KotlinBuilderActionTests.java index a26c1a1..bc08407 100644 --- a/kotlin/builder/integrationtests/KotlinBuilderActionTests.java +++ b/kotlin/builder/integrationtests/KotlinBuilderActionTests.java
@@ -1,5 +1,6 @@ package io.bazel.kotlin.builder; +import io.bazel.kotlin.builder.mode.jvm.actions.JacocoProcessor; import io.bazel.kotlin.builder.mode.jvm.actions.KotlinCompiler; import org.junit.Test; @@ -11,4 +12,14 @@ assertFileExists(DirectoryType.CLASSES, "something/AClass.class"); assertFileDoesNotExist(outputs().getJar()); } + + @Test + public void testCoverage() { + addSource("AClass.kt", "package something;" + "class AClass{}"); + instance(KotlinCompiler.class).compile(builderCommand()); + instance(JacocoProcessor.class).instrument(builderCommand()); + assertFileExists(DirectoryType.CLASSES, "something/AClass.class"); + assertFileExists(DirectoryType.CLASSES, "something/AClass.class.uninstrumented"); + assertFileDoesNotExist(outputs().getJar()); + } }
diff --git a/kotlin/builder/integrationtests/KotlinBuilderTestCase.java b/kotlin/builder/integrationtests/KotlinBuilderTestCase.java index f1cc005..015310c 100644 --- a/kotlin/builder/integrationtests/KotlinBuilderTestCase.java +++ b/kotlin/builder/integrationtests/KotlinBuilderTestCase.java
@@ -29,6 +29,10 @@ private String label = null; private Path inputSourceDir = null; + protected void setPostProcessor(String postProcessor) { + builder.setInfo(builder.getInfo().toBuilder().setPostProcessor(postProcessor)); + } + @Before public void setupNext() { resetTestContext("a_test_" + counter.incrementAndGet()); @@ -159,6 +163,11 @@ assertFileExists(file.toString()); } + void assertFileDoesNotExist(DirectoryType dir, String filePath) { + Path file = DirectoryType.select(dir, builderCommand()).resolve(filePath); + assertFileDoesNotExist(file.toString()); + } + void assertFileDoesNotExist(String filePath) { assertWithMessage("file exisst: " + filePath).that(new File(filePath).exists()).isFalse(); }
diff --git a/kotlin/builder/integrationtests/KotlinBuilderTests.java b/kotlin/builder/integrationtests/KotlinBuilderTests.java index cb5aa62..053abe0 100644 --- a/kotlin/builder/integrationtests/KotlinBuilderTests.java +++ b/kotlin/builder/integrationtests/KotlinBuilderTests.java
@@ -21,6 +21,7 @@ addSource("AClass.kt", "package something;" + "class AClass{}"); runCompileTask(); assertFileExists(DirectoryType.CLASSES, "something/AClass.class"); + assertFileDoesNotExist(DirectoryType.CLASSES, "something/AClass.class.uninstrumented"); } @Test @@ -33,6 +34,15 @@ assertFileExists(outputs().getJar()); } + @Test + public void testCoverage() { + setPostProcessor("jacoco"); + addSource("AClass.kt", "package something;" + "class AClass{}"); + runCompileTask(); + assertFileExists(DirectoryType.CLASSES, "something/AClass.class"); + assertFileExists(DirectoryType.CLASSES, "something/AClass.class.uninstrumented"); + } + private void runCompileTask() { KotlinModel.BuilderCommand command = builderCommand(); for (DirectoryType directoryType : DirectoryType.values()) {
diff --git a/kotlin/builder/proto/BUILD b/kotlin/builder/proto/BUILD index 8bc369b..4682b52 100644 --- a/kotlin/builder/proto/BUILD +++ b/kotlin/builder/proto/BUILD
@@ -35,7 +35,7 @@ # name="%s_java_proto" % lib, # deps=["%s_proto" % lib], # ) for lib in _PROTO_LIBS] -# + java_import( name = "deps",
diff --git a/kotlin/builder/proto/jars/libkotlin_model_proto-speed.jar b/kotlin/builder/proto/jars/libkotlin_model_proto-speed.jar index 943c343..d04c1b2 100755 --- a/kotlin/builder/proto/jars/libkotlin_model_proto-speed.jar +++ b/kotlin/builder/proto/jars/libkotlin_model_proto-speed.jar Binary files differ
diff --git a/kotlin/builder/proto/kotlin_model.proto b/kotlin/builder/proto/kotlin_model.proto index d1e4ced..22760fd 100644 --- a/kotlin/builder/proto/kotlin_model.proto +++ b/kotlin/builder/proto/kotlin_model.proto
@@ -87,6 +87,8 @@ // Jars that the kotlin compiler will allow package private access to. repeated string friend_paths = 10; + + string post_processor = 11; } // Directories used by the builder.
diff --git a/kotlin/builder/src/io/bazel/kotlin/builder/BuildCommandBuilder.kt b/kotlin/builder/src/io/bazel/kotlin/builder/BuildCommandBuilder.kt index 54302c1..335cd02 100644 --- a/kotlin/builder/src/io/bazel/kotlin/builder/BuildCommandBuilder.kt +++ b/kotlin/builder/src/io/bazel/kotlin/builder/BuildCommandBuilder.kt
@@ -123,6 +123,7 @@ } passthroughFlags = argMap.optionalSingle("--kotlin_passthrough_flags") addAllFriendPaths(argMap.mandatory("--kotlin_friend_paths")) + postProcessor = argMap.optionalSingle("--post_processor") ?: "" toolchainInfoBuilder.commonBuilder.apiVersion = argMap.mandatorySingle("--kotlin_api_version") toolchainInfoBuilder.commonBuilder.languageVersion = argMap.mandatorySingle("--kotlin_language_version") toolchainInfoBuilder.jvmBuilder.jvmTarget = argMap.mandatorySingle("--kotlin_jvm_target")
diff --git a/kotlin/builder/src/io/bazel/kotlin/builder/mode/jvm/KotlinJvmCompilationExecutor.kt b/kotlin/builder/src/io/bazel/kotlin/builder/mode/jvm/KotlinJvmCompilationExecutor.kt index db1b42d..8f1f7d0 100644 --- a/kotlin/builder/src/io/bazel/kotlin/builder/mode/jvm/KotlinJvmCompilationExecutor.kt +++ b/kotlin/builder/src/io/bazel/kotlin/builder/mode/jvm/KotlinJvmCompilationExecutor.kt
@@ -26,6 +26,7 @@ import io.bazel.kotlin.builder.mode.jvm.actions.JavaCompiler import io.bazel.kotlin.builder.mode.jvm.actions.KotlinCompiler import io.bazel.kotlin.builder.mode.jvm.actions.OutputJarCreator +import io.bazel.kotlin.builder.mode.jvm.actions.JacocoProcessor import io.bazel.kotlin.builder.mode.jvm.utils.KotlinCompilerOutputSink import io.bazel.kotlin.model.KotlinModel.BuilderCommand import java.io.File @@ -45,7 +46,8 @@ private val outputSink: KotlinCompilerOutputSink, private val javaCompiler: JavaCompiler, private val jDepsGenerator: JDepsGenerator, - private val outputJarCreator: OutputJarCreator + private val outputJarCreator: OutputJarCreator, + private val jacocoProcessor: JacocoProcessor ) : KotlinJvmCompilationExecutor { override fun compile(command: BuilderCommand): Result { val context = Context() @@ -53,6 +55,11 @@ runAnnotationProcessors(command) } compileClasses(context, commandWithApSources) + if (command.info.postProcessor == "jacoco") { + context.execute("instrument class files") { + jacocoProcessor.instrument(commandWithApSources) + } + } context.execute("create jar") { outputJarCreator.createOutputJar(commandWithApSources) }
diff --git a/kotlin/builder/src/io/bazel/kotlin/builder/mode/jvm/actions/JacocoProcessor.kt b/kotlin/builder/src/io/bazel/kotlin/builder/mode/jvm/actions/JacocoProcessor.kt new file mode 100644 index 0000000..abd0c9f --- /dev/null +++ b/kotlin/builder/src/io/bazel/kotlin/builder/mode/jvm/actions/JacocoProcessor.kt
@@ -0,0 +1,67 @@ +/* + * 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. + */ +package io.bazel.kotlin.builder.mode.jvm.actions + +import io.bazel.kotlin.builder.KotlinToolchain +import org.jacoco.core.instr.Instrumenter +import org.jacoco.core.runtime.OfflineInstrumentationAccessGenerator +import java.io.BufferedInputStream +import java.io.BufferedOutputStream +import java.io.IOException +import java.nio.file.FileVisitResult +import java.nio.file.Files +import java.nio.file.Path +import java.nio.file.Paths +import java.nio.file.SimpleFileVisitor +import java.nio.file.attribute.BasicFileAttributes +import io.bazel.kotlin.model.KotlinModel +import com.google.devtools.build.lib.view.proto.Deps +import com.google.inject.ImplementedBy +import com.google.inject.Inject + +@ImplementedBy(DefaultJacocoProcessor::class) +interface JacocoProcessor { + fun instrument(command: KotlinModel.BuilderCommand) +} + +class DefaultJacocoProcessor @Inject constructor( + val compiler: KotlinToolchain.KotlincInvoker +) : JacocoProcessor { + override fun instrument(command: KotlinModel.BuilderCommand) { + val classDir = Paths.get(command.directories.classes) + val instr = Instrumenter(OfflineInstrumentationAccessGenerator()) + + // Runs Jacoco instrumentation processor over all .class files. + Files.walkFileTree( + classDir, + object : SimpleFileVisitor<Path>() { + override fun visitFile(file: Path, attrs: BasicFileAttributes): FileVisitResult { + if (!file.fileName.toString().endsWith(".class")) { + return FileVisitResult.CONTINUE + } + + val uninstrumentedCopy = Paths.get(file.toString() + ".uninstrumented") + Files.move(file, uninstrumentedCopy) + BufferedInputStream(Files.newInputStream(uninstrumentedCopy)).use { input -> + BufferedOutputStream(Files.newOutputStream(file)).use { output -> + instr.instrument(input, output, file.toString()) + } + } + return FileVisitResult.CONTINUE + } + }) + } +}
diff --git a/kotlin/internal/compile.bzl b/kotlin/internal/compile.bzl index e08429a..94b68ea 100644 --- a/kotlin/internal/compile.bzl +++ b/kotlin/internal/compile.bzl
@@ -68,6 +68,10 @@ if len(plugin_info.annotation_processors) > 0: args += [ "--kt-plugins", plugin_info.to_json() ] + # Post-process class files with the Jacoco offline instrumenter, if needed. + if ctx.coverage_instrumented() or ctx.attr.internal_coverage_instrumented: + args += [ "--post_processor", "jacoco" ] + # Declare and write out argument file. args_file = ctx.actions.declare_file(ctx.label.name + ".jar-2.params") ctx.actions.write(args_file, "\n".join(args)) @@ -144,7 +148,7 @@ transitive_runtime_jars = my_transitive_runtime_jars ) -def _make_providers(ctx, java_info, module_name, transitive_files=depset(order="default")): +def _make_providers(ctx, java_info, module_name, transitive_files=depset(order="default"), extra_runfiles=[]): kotlin_info=kt.info.KtInfo( srcs=ctx.files.srcs, module_name = module_name, @@ -159,9 +163,13 @@ ), ) + files = [ctx.outputs.jar] + if hasattr(ctx.outputs, "executable"): + files.append(ctx.outputs.executable) default_info = DefaultInfo( - files=depset([ctx.outputs.jar]), + files=depset(files), runfiles=ctx.runfiles( + files=extra_runfiles + [ctx.outputs.jar], transitive_files=transitive_files, collect_default=True ), @@ -170,6 +178,11 @@ return struct( kt=kotlin_info, providers=[java_info,default_info,kotlin_info], + instrumented_files = struct( + extensions = ['.kt'], + source_attributes = ['srcs'], + dependency_attributes = ['deps', 'runtime_deps'], + ) ) def _compile_action(ctx, rule_kind, module_name, friend_paths=depset(), src_jars=[]):
diff --git a/kotlin/internal/rules.bzl b/kotlin/internal/rules.bzl index 79ee8b1..95e441d 100644 --- a/kotlin/internal/rules.bzl +++ b/kotlin/internal/rules.bzl
@@ -77,21 +77,7 @@ def kt_jvm_library_impl(ctx): module_name=utils.derive_module_name(ctx) - return compile.make_providers( - ctx, - compile.compile_action(ctx, "kt_jvm_library", module_name), - module_name, - ) - -def kt_jvm_binary_impl(ctx): - module_name=utils.derive_module_name(ctx) - java_info = compile.compile_action(ctx, "kt_jvm_binary", module_name) - utils.actions.write_launcher( - ctx, - java_info.transitive_runtime_jars, - ctx.attr.main_class, - ctx.attr.jvm_flags - ) + java_info = compile.compile_action(ctx, "kt_jvm_library", module_name) return compile.make_providers( ctx, java_info, @@ -99,8 +85,42 @@ depset( order = "default", transitive=[java_info.transitive_runtime_jars], - direct=[ctx.executable._java] - ), + ) + ) + +def _kt_jvm_runnable_impl(ctx, rule_kind, module_name, launcher_jvm_flags=[], friend_paths=depset()): + java_info = compile.compile_action(ctx, rule_kind, module_name, friend_paths) + + transitive_runtime_jars = java_info.transitive_runtime_jars + if rule_kind == "kt_jvm_test": + transitive_runtime_jars += ctx.files._bazel_test_runner + if ctx.configuration.coverage_enabled or ctx.attr.internal_coverage_enabled: + transitive_runtime_jars += ctx.files._jacocorunner + + extra_runfiles = utils.actions.write_launcher( + ctx, + transitive_runtime_jars, + main_class = ctx.attr.main_class, + jvm_flags = launcher_jvm_flags + ctx.attr.jvm_flags, + ) + transitive_files = depset( + order = "default", + transitive=[transitive_runtime_jars], + direct=[ctx.executable._java], + ) + return compile.make_providers( + ctx, + java_info, + module_name, + transitive_files, + extra_runfiles, + ) + +def kt_jvm_binary_impl(ctx): + return _kt_jvm_runnable_impl( + ctx, + "kt_jvm_binary", + utils.derive_module_name(ctx) ) def kt_jvm_junit_test_impl(ctx): @@ -117,24 +137,10 @@ friend_paths += [j.path for j in friends[0][JavaInfo].compile_jars] module_name = friends[0][kt.info.KtInfo].module_name - java_info = compile.compile_action(ctx, "kt_jvm_test", module_name,friend_paths) - - transitive_runtime_jars = java_info.transitive_runtime_jars + ctx.files._bazel_test_runner - launcherJvmFlags = ["-ea", "-Dbazel.test_suite=%s"% ctx.attr.test_class] - - utils.actions.write_launcher( + return _kt_jvm_runnable_impl( ctx, - transitive_runtime_jars, - main_class = ctx.attr.main_class, - jvm_flags = launcherJvmFlags + ctx.attr.jvm_flags, + rule_kind = "kt_jvm_test", + module_name = module_name, + launcher_jvm_flags = ["-ea", "-Dbazel.test_suite=%s" % ctx.attr.test_class], + friend_paths=friend_paths ) - return compile.make_providers( - ctx, - java_info, - module_name, - depset( - order = "default", - transitive=[transitive_runtime_jars], - direct=[ctx.executable._java] - ), - ) \ No newline at end of file
diff --git a/kotlin/internal/utils.bzl b/kotlin/internal/utils.bzl index fd34a66..724e558 100644 --- a/kotlin/internal/utils.bzl +++ b/kotlin/internal/utils.bzl
@@ -214,20 +214,51 @@ jvm_flags = " ".join([ctx.expand_location(f, ctx.attr.data) for f in jvm_flags]) template = ctx.attr._java_stub_template.files.to_list()[0] + workspace_prefix = ctx.workspace_name + "/" + substitutions = { + "%classpath%": classpath, + "%javabin%": "JAVABIN=${RUNPATH}" + ctx.executable._java.short_path, + "%jvm_flags%": jvm_flags, + "%workspace_prefix%": workspace_prefix, + } + + extra_runfiles = [] + if ctx.configuration.coverage_enabled or ctx.attr.internal_coverage_enabled: + metadata = ctx.new_file("coverage_runtime_classpath/%s/runtime-classpath.txt" % ctx.attr.name) + extra_runfiles.append(metadata) + # We replace '../' to get a runtime-classpath.txt as close as possible to the one + # produced by java_binary. + metadata_entries = [rjar.short_path.replace("../", "external/") for rjar in rjars] + ctx.file_action(metadata, content="\n".join(metadata_entries)) + substitutions += { + "%java_start_class%": "com.google.testing.coverage.JacocoCoverageRunner", + # %set_jacoco_main_class% and %set_jacoco_java_runfiles_root% are not + # taken into account, so we cram everything with %set_jacoco_metadata%. + "%set_jacoco_metadata%": "\n".join([ + "export JACOCO_METADATA_JAR=${JAVA_RUNFILES}/" + workspace_prefix + metadata.short_path, + "export JACOCO_MAIN_CLASS=" + main_class, + "export JACOCO_JAVA_RUNFILES_ROOT=${JAVA_RUNFILES}/" + workspace_prefix, + ]), + "%set_jacoco_main_class%": "", + "%set_jacoco_java_runfiles_root%": "", + } + else: + substitutions += { + "%java_start_class%": main_class, + "%set_jacoco_metadata%": "", + "%set_jacoco_main_class%": "", + "%set_jacoco_java_runfiles_root%": "", + } + ctx.actions.expand_template( template = template, output = ctx.outputs.executable, - substitutions = { - "%classpath%": classpath, - "%java_start_class%": main_class, - "%javabin%": "JAVABIN=${RUNPATH}" + ctx.executable._java.short_path, - "%jvm_flags%": jvm_flags, - "%set_jacoco_metadata%": "", - "%workspace_prefix%": ctx.workspace_name + "/", - }, + substitutions = substitutions, is_executable = True, ) + return extra_runfiles + # EXPORT ####################################################################################################################################################### utils = struct( actions = struct(
diff --git a/kotlin/kotlin.bzl b/kotlin/kotlin.bzl index 2ecd9a5..79c6fee 100644 --- a/kotlin/kotlin.bzl +++ b/kotlin/kotlin.bzl
@@ -176,12 +176,28 @@ aspects = [_kt_jvm_plugin_aspect], ), "module_name": attr.string(), + "internal_coverage_instrumented": attr.bool( + default = False, + doc = "visible for testing", + ), }.items()) _runnable_common_attr = dict(_common_attr.items() + { "jvm_flags": attr.string_list( default = [], ), + "_jacocorunner": attr.label( + default = Label("@bazel_tools//tools/jdk:JacocoCoverage"), + ), + "_lcov_merger": attr.label( + default = Label("@bazel_tools//tools/test:LcovMerger"), + executable = True, + cfg = "target", + ), + "internal_coverage_enabled": attr.bool( + default = False, + doc = "visible for testing", + ), }.items()) ########################################################################################################################
diff --git a/tests/integrationtests/jvm/BUILD b/tests/integrationtests/jvm/BUILD index 70bd38a..f86d868 100644 --- a/tests/integrationtests/jvm/BUILD +++ b/tests/integrationtests/jvm/BUILD
@@ -33,12 +33,22 @@ data = [ "//examples/dagger:coffee_app"] ) +kt_it_assertion_test( + name = "coverage_tests", + cases = "//tests/integrationtests/jvm/coverage:cases", + test_class="io.bazel.kotlin.testing.jvm.JvmCoverageFunctionalTests", + deps = [ + "//tests/integrationtests/jvm/coverage:cases", + ], +) + test_suite( name = "jvm", tests = [ ":basic_tests", ":kapt_tests", ":example_tests", + ":coverage_tests", "//tests/integrationtests/jvm/basic:friends_tests" ] )
diff --git a/tests/integrationtests/jvm/JvmCoverageFunctionalTests.kt b/tests/integrationtests/jvm/JvmCoverageFunctionalTests.kt new file mode 100644 index 0000000..1459117 --- /dev/null +++ b/tests/integrationtests/jvm/JvmCoverageFunctionalTests.kt
@@ -0,0 +1,56 @@ +/* + * 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. + */ +package io.bazel.kotlin.testing.jvm + +import com.google.common.truth.Truth.assertWithMessage +import io.bazel.kotlin.testing.AssertionTestCase +import org.junit.Test +import java.nio.file.Files +import java.nio.file.Paths +import java.util.stream.Collectors + +class JvmCoverageFunctionalTests : AssertionTestCase("tests/integrationtests/jvm/coverage") { + + @Test + fun testCoverage() { + val coverageOutputDir = Files.createTempDirectory( + Paths.get(System.getenv("TEST_TMPDIR")), + "coverage") + assertExecutableRunfileSucceeds( + "foo_test", + description = "Code coverage should by default be collected for :foo but not :foo_test", + environment = mapOf( + "JAVA_COVERAGE_FILE" to coverageOutputDir.resolve("coverage.dat").toString(), + "RUNPATH" to Paths.get("").toAbsolutePath().toString() + "/")) + val coverageReports = Files.list(coverageOutputDir) + .filter { it.toString().endsWith(".dat") } + .collect(Collectors.toList()) + assertWithMessage("expected one and only one coverage report").that(coverageReports).hasSize(1) + val coverageReportLines = Files.readAllLines(coverageReports[0]) + assertWithMessage("unexpected coverage report").that(coverageReportLines).isEqualTo(listOf( + "SF:simple/Foo.kt", + "FN:4,simple/Foo::exampleA ()Ljava/lang/String;", + "FNDA:1,simple/Foo::exampleA ()Ljava/lang/String;", + "FN:5,simple/Foo::exampleB ()Ljava/lang/String;", + "FNDA:0,simple/Foo::exampleB ()Ljava/lang/String;", + "FN:3,simple/Foo::<init> ()V", + "FNDA:1,simple/Foo::<init> ()V", + "DA:3,3", + "DA:4,2", + "DA:5,0", + "end_of_record")) + } +}
diff --git a/tests/integrationtests/jvm/coverage/BUILD b/tests/integrationtests/jvm/coverage/BUILD new file mode 100644 index 0000000..b906987 --- /dev/null +++ b/tests/integrationtests/jvm/coverage/BUILD
@@ -0,0 +1,28 @@ +load("@io_bazel_rules_kotlin//kotlin:kotlin.bzl", "kt_jvm_library", "kt_jvm_test") + +kt_jvm_library( + name = "foo", + srcs = ["Foo.kt"], + internal_coverage_instrumented = True, +) + +kt_jvm_test( + name = "foo_test", + srcs = ["FooTest.kt"], + test_class = "simple.FooTest", + deps = [ + ":foo", + "@io_bazel_rules_kotlin_junit_junit//jar", + ], + size = "small", + internal_coverage_enabled = True, +) + +filegroup( + name = "cases", + srcs = [ + ":foo_test", + ], + visibility=["//tests/integrationtests:__subpackages__"], + testonly = True, +)
diff --git a/tests/integrationtests/jvm/coverage/Foo.kt b/tests/integrationtests/jvm/coverage/Foo.kt new file mode 100644 index 0000000..d4246a7 --- /dev/null +++ b/tests/integrationtests/jvm/coverage/Foo.kt
@@ -0,0 +1,6 @@ +package simple + +class Foo { + fun exampleA() = "A" + fun exampleB() = "B" // Not covered +}
diff --git a/tests/integrationtests/jvm/coverage/FooTest.kt b/tests/integrationtests/jvm/coverage/FooTest.kt new file mode 100644 index 0000000..44816a3 --- /dev/null +++ b/tests/integrationtests/jvm/coverage/FooTest.kt
@@ -0,0 +1,11 @@ +package simple + +import org.junit.Assert.assertEquals +import org.junit.Test + +class FooTest { + @Test + fun exampleA() { + assertEquals("A", Foo().exampleA()) + } +}
diff --git a/tests/rules/AssertionTestCase.kt b/tests/rules/AssertionTestCase.kt index 0e44ea7..965bc93 100644 --- a/tests/rules/AssertionTestCase.kt +++ b/tests/rules/AssertionTestCase.kt
@@ -82,10 +82,16 @@ } abstract class BasicAssertionTestCase { - protected fun assertExecutableRunfileSucceeds(executable: String, description: String? = null) { + protected fun assertExecutableRunfileSucceeds( + executable: String, + description: String? = null, + environment: Map<String, String> = emptyMap() + ) { ProcessBuilder().command("bash", "-c", Paths.get(executable).fileName.toString()) .also { it.directory(executable.resolveDirectory()) } - .start().let { + .also { it.environment().putAll(environment) } + .also { it.inheritIO() } + .start().also { it.waitFor(5, TimeUnit.SECONDS) assert(it.exitValue() == 0) { throw TestCaseFailedException(description, RuntimeException("non-zero return code: ${it.exitValue()}"))
diff --git a/tests/rules/defs.bzl b/tests/rules/defs.bzl index 95be157..577dd52 100644 --- a/tests/rules/defs.bzl +++ b/tests/rules/defs.bzl
@@ -16,7 +16,8 @@ _TEST_COMMON_DEPS=[ "//tests/rules:assertion_test_case", "//third_party/jvm/com/google/truth", - "//third_party/jvm/junit:junit" + "//third_party/jvm/junit:junit", + "@com_github_jetbrains_kotlin//:kotlin-test", ] def kt_it_assertion_test(name, test_class, cases=None, data = [], deps=[]):
diff --git a/third_party/dependencies.yaml b/third_party/dependencies.yaml index c7d68ea..a08d35b 100644 --- a/third_party/dependencies.yaml +++ b/third_party/dependencies.yaml
@@ -45,6 +45,15 @@ modules: ["", "compiler", "producers"] lang: "java" version: "2.16" + org.jacoco: + org.jacoco.core: + lang: "java" + version: "0.7.5.201505241946" + exclude: ["org.ow2.asm:asm-debug-all"] + org.ow2.asm: + asm-commons: + lang: "java" + version: "6.0" org.jetbrains.kotlinx: kotlinx-coroutines: modules: ["core"]
diff --git a/third_party/jvm/org/jacoco/BUILD b/third_party/jvm/org/jacoco/BUILD new file mode 100644 index 0000000..be35a9f --- /dev/null +++ b/third_party/jvm/org/jacoco/BUILD
@@ -0,0 +1,12 @@ +licenses(["notice"]) +java_library( + name = "org_jacoco_core", + exports = [ + "//external:jar/io_bazel_rules_kotlin_org/jacoco/org_jacoco_core" + ], + visibility = [ + "//visibility:public" + ] +) + +
diff --git a/third_party/jvm/org/ow2/asm/BUILD b/third_party/jvm/org/ow2/asm/BUILD new file mode 100644 index 0000000..d3c8dde --- /dev/null +++ b/third_party/jvm/org/ow2/asm/BUILD
@@ -0,0 +1,42 @@ +licenses(["notice"]) +java_library( + name = "asm", + exports = [ + "//external:jar/io_bazel_rules_kotlin_org/ow2/asm/asm" + ], + visibility = [ + "//visibility:public" + ] +) + + + +java_library( + name = "asm_commons", + exports = [ + "//external:jar/io_bazel_rules_kotlin_org/ow2/asm/asm_commons" + ], + runtime_deps = [ + ":asm_tree" + ], + visibility = [ + "//visibility:public" + ] +) + + + +java_library( + name = "asm_tree", + exports = [ + "//external:jar/io_bazel_rules_kotlin_org/ow2/asm/asm_tree" + ], + runtime_deps = [ + ":asm" + ], + visibility = [ + "//visibility:public" + ] +) + +
diff --git a/third_party/jvm/workspace.bzl b/third_party/jvm/workspace.bzl index dac3946..10df4ca 100644 --- a/third_party/jvm/workspace.bzl +++ b/third_party/jvm/workspace.bzl
@@ -51,10 +51,14 @@ {"artifact": "org.checkerframework:checker-compat-qual:2.3.0", "lang": "java", "sha1": "69cb4fea55a9d89b8827d107f17c985cc1a76052", "repository": "https://repo.maven.apache.org/maven2/", "name": "io_bazel_rules_kotlin_org_checkerframework_checker_compat_qual", "actual": "@io_bazel_rules_kotlin_org_checkerframework_checker_compat_qual//jar", "bind": "jar/io_bazel_rules_kotlin_org/checkerframework/checker_compat_qual"}, {"artifact": "org.codehaus.mojo:animal-sniffer-annotations:1.14", "lang": "java", "sha1": "775b7e22fb10026eed3f86e8dc556dfafe35f2d5", "repository": "https://repo.maven.apache.org/maven2/", "name": "io_bazel_rules_kotlin_org_codehaus_mojo_animal_sniffer_annotations", "actual": "@io_bazel_rules_kotlin_org_codehaus_mojo_animal_sniffer_annotations//jar", "bind": "jar/io_bazel_rules_kotlin_org/codehaus/mojo/animal_sniffer_annotations"}, {"artifact": "org.hamcrest:hamcrest-core:1.3", "lang": "java", "sha1": "42a25dc3219429f0e5d060061f71acb49bf010a0", "repository": "https://repo.maven.apache.org/maven2/", "name": "io_bazel_rules_kotlin_org_hamcrest_hamcrest_core", "actual": "@io_bazel_rules_kotlin_org_hamcrest_hamcrest_core//jar", "bind": "jar/io_bazel_rules_kotlin_org/hamcrest/hamcrest_core"}, + {"artifact": "org.jacoco:org.jacoco.core:0.7.5.201505241946", "lang": "java", "sha1": "1ea906dc5201d2a1bc0604f8650534d4bcaf4c95", "repository": "https://repo.maven.apache.org/maven2/", "name": "io_bazel_rules_kotlin_org_jacoco_org_jacoco_core", "actual": "@io_bazel_rules_kotlin_org_jacoco_org_jacoco_core//jar", "bind": "jar/io_bazel_rules_kotlin_org/jacoco/org_jacoco_core"}, {"artifact": "org.jetbrains.kotlin:kotlin-stdlib-common:1.2.41", "lang": "java", "sha1": "bf0bdac1048fd1c5c54362978dd7e06bd2230e78", "repository": "https://repo.maven.apache.org/maven2/", "name": "io_bazel_rules_kotlin_org_jetbrains_kotlin_kotlin_stdlib_common", "actual": "@io_bazel_rules_kotlin_org_jetbrains_kotlin_kotlin_stdlib_common//jar", "bind": "jar/io_bazel_rules_kotlin_org/jetbrains/kotlin/kotlin_stdlib_common"}, {"artifact": "org.jetbrains.kotlinx:atomicfu-common:0.10.1", "lang": "java", "sha1": "4eb87291dff597f2f5bac4876fae02ef23466a39", "repository": "https://repo.maven.apache.org/maven2/", "name": "io_bazel_rules_kotlin_org_jetbrains_kotlinx_atomicfu_common", "actual": "@io_bazel_rules_kotlin_org_jetbrains_kotlinx_atomicfu_common//jar", "bind": "jar/io_bazel_rules_kotlin_org/jetbrains/kotlinx/atomicfu_common"}, {"artifact": "org.jetbrains.kotlinx:kotlinx-coroutines-core-common:0.23.1", "lang": "java", "sha1": "ee988a3e0a918579315ce6654f415b47fec39d36", "repository": "https://repo.maven.apache.org/maven2/", "name": "io_bazel_rules_kotlin_org_jetbrains_kotlinx_kotlinx_coroutines_core_common", "actual": "@io_bazel_rules_kotlin_org_jetbrains_kotlinx_kotlinx_coroutines_core_common//jar", "bind": "jar/io_bazel_rules_kotlin_org/jetbrains/kotlinx/kotlinx_coroutines_core_common"}, {"artifact": "org.jetbrains.kotlinx:kotlinx-coroutines-core:0.23.1", "lang": "java", "sha1": "fb67b623766f0b2d56697f0b8ed14450f285b8ed", "repository": "https://repo.maven.apache.org/maven2/", "name": "io_bazel_rules_kotlin_org_jetbrains_kotlinx_kotlinx_coroutines_core", "actual": "@io_bazel_rules_kotlin_org_jetbrains_kotlinx_kotlinx_coroutines_core//jar", "bind": "jar/io_bazel_rules_kotlin_org/jetbrains/kotlinx/kotlinx_coroutines_core"}, + {"artifact": "org.ow2.asm:asm-commons:6.0", "lang": "java", "sha1": "f256fd215d8dd5a4fa2ab3201bf653de266ed4ec", "repository": "https://repo.maven.apache.org/maven2/", "name": "io_bazel_rules_kotlin_org_ow2_asm_asm_commons", "actual": "@io_bazel_rules_kotlin_org_ow2_asm_asm_commons//jar", "bind": "jar/io_bazel_rules_kotlin_org/ow2/asm/asm_commons"}, + {"artifact": "org.ow2.asm:asm-tree:6.0", "lang": "java", "sha1": "a624f1a6e4e428dcd680a01bab2d4c56b35b18f0", "repository": "https://repo.maven.apache.org/maven2/", "name": "io_bazel_rules_kotlin_org_ow2_asm_asm_tree", "actual": "@io_bazel_rules_kotlin_org_ow2_asm_asm_tree//jar", "bind": "jar/io_bazel_rules_kotlin_org/ow2/asm/asm_tree"}, + {"artifact": "org.ow2.asm:asm:6.0", "lang": "java", "sha1": "bc6fa6b19424bb9592fe43bbc20178f92d403105", "repository": "https://repo.maven.apache.org/maven2/", "name": "io_bazel_rules_kotlin_org_ow2_asm_asm", "actual": "@io_bazel_rules_kotlin_org_ow2_asm_asm//jar", "bind": "jar/io_bazel_rules_kotlin_org/ow2/asm/asm"}, ] def maven_dependencies(callback = declare_maven):