From 9eaa5778b7fd08b2478952b0ac35bb3479370837 Mon Sep 17 00:00:00 2001 From: "Sergey.Shanshin" Date: Mon, 12 Feb 2024 16:07:49 +0100 Subject: [PATCH 1/3] Implemented Kover Features artifact to integration with Android Gradle Plugin 1. Created published Kover artifact kover-features-java 2. Added Java API for offline instrumentation of single class 3. Created kotlinx.kover.features.java.KoverLegacyFeatures and put the methods used by the Kover CLI there 4. Removed the dependency on intellij-coverage-reporter from the Kover CLI and add the dependency on kover-features-java Resolves #534 --- docs/index.md | 3 +- docs/offline-instrumentation/index.md | 34 ++++- kover-cli/build.gradle.kts | 3 +- .../cli/commands/OfflineInstrumentCommand.kt | 22 +-- .../kover/cli/commands/ReportCommand.kt | 19 +-- .../kotlinx/kover/cli/util/KoverUtils.kt | 4 +- kover-features-java/build.gradle.kts | 46 ++++++ .../kover/features/java/ConDySettings.java | 36 +++++ .../kover/features/java/KoverFeatures.java | 42 ++++++ .../features/java/KoverLegacyFeatures.java | 134 ++++++++++++++++++ .../features/java/OfflineInstrumenter.java | 19 +++ .../java/OfflineInstrumenterImpl.java | 34 +++++ .../src/main/resources/kover.version | 1 + kover-offline-runtime/build.gradle.kts | 8 +- .../kover/offline/runtime/KoverInit.java | 16 ++- .../offline/runtime/api/KoverRuntime.java | 9 +- settings.gradle.kts | 1 + 17 files changed, 381 insertions(+), 50 deletions(-) create mode 100644 kover-features-java/build.gradle.kts create mode 100644 kover-features-java/src/main/java/kotlinx/kover/features/java/ConDySettings.java create mode 100644 kover-features-java/src/main/java/kotlinx/kover/features/java/KoverFeatures.java create mode 100644 kover-features-java/src/main/java/kotlinx/kover/features/java/KoverLegacyFeatures.java create mode 100644 kover-features-java/src/main/java/kotlinx/kover/features/java/OfflineInstrumenter.java create mode 100644 kover-features-java/src/main/java/kotlinx/kover/features/java/OfflineInstrumenterImpl.java create mode 100644 kover-features-java/src/main/resources/kover.version diff --git a/docs/index.md b/docs/index.md index 988b5e49..70e8cbec 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,2 +1,3 @@ - [Kover Gradle Plugin](gradle-plugin) -- [Kover Command Line Interface](cli) \ No newline at end of file +- [Kover Command Line Interface](cli) +- [Kover offline instrumentation](offline-instrumentation) \ No newline at end of file diff --git a/docs/offline-instrumentation/index.md b/docs/offline-instrumentation/index.md index 8db28367..8ad66488 100644 --- a/docs/offline-instrumentation/index.md +++ b/docs/offline-instrumentation/index.md @@ -11,12 +11,32 @@ Offline instrumentation is suitable when using runtime environments that do not ### Class instrumentation -For instrumentation, you must first build the application, then the root directories for the class files -must be passed to Kover CLI as arguments, see [Kover CLI](../cli#offline-instrumentation) for the technical detils. +#### Instrumentation by Kover CLI +The Kover CLI is a fat jar that needs to be called and passed certain commands through arguments. + +For instrumentation, you must first build the application, then the root directories for the class files +must be passed to Kover CLI as arguments, see [Kover CLI](../cli#offline-instrumentation) for the technical details. + +#### Instrumentation by Kover Features +Kover Features is an artifact with Java classes that provide capabilities of the Kover library. + +Dependency on Kover Features `org.jetbrains.kotlinx:kover-features-java:0.7.5`. + +Then you need to use the Kover Features classes to instrument the bytecode of each class: +```kotlin +import kotlinx.kover.features.java.KoverFeatures + // ... + + val instrumenter = KoverFeatures.createOfflineInstrumenter() + + // read class-file with name `fileName` bytes to `classBytes` + val instrumentedBytes = instrumenter.instrument(classBytes, fileName) + // save `instrumentedBytes` to file +``` ### Dump coverage result -To run classes instrumented offline, you'll need to add `org.jetbrains.kotlinx:kover-offline` artifact to the application's classpath. +To run classes instrumented offline, you'll need to add `org.jetbrains.kotlinx:kover-offline-runtime` artifact to the application's classpath. There are several ways to get coverage: @@ -64,16 +84,18 @@ Calling these methods is allowed only after all tests are completed. If the meth See [example](#example-of-using-the-api). ## Logging -`org.jetbrains.kotlinx:kover-offline` has its own logging system. +`org.jetbrains.kotlinx:kover-offline-runtime` has its own logging system. + +By default, warning and error messages are printed to standard error stream. -By default, error messages are saved to a file in the working directory with the name `kover-offline.log`. To change the path to this file, pass the `kover.offline.log.file.path` system property with new path. +It is also possible to save all log messages to a file, to do this, you need to pass the system property `kover.offline.log.file.path` with path to the log file. ## Examples ### Gradle example for binary report Example of a custom binary report production using Kover tool CLI in Gradle -``` +```kotlin plugins { kotlin("jvm") version "1.8.0" application diff --git a/kover-cli/build.gradle.kts b/kover-cli/build.gradle.kts index aa9606d5..1f33a8e5 100644 --- a/kover-cli/build.gradle.kts +++ b/kover-cli/build.gradle.kts @@ -33,7 +33,8 @@ kotlin { } dependencies { - implementation(libs.intellij.reporter) + implementation(project(":kover-features-java")) + implementation(libs.args4j) testImplementation(kotlin("test")) diff --git a/kover-cli/src/main/kotlin/kotlinx/kover/cli/commands/OfflineInstrumentCommand.kt b/kover-cli/src/main/kotlin/kotlinx/kover/cli/commands/OfflineInstrumentCommand.kt index 76e3665e..6ee71d72 100644 --- a/kover-cli/src/main/kotlin/kotlinx/kover/cli/commands/OfflineInstrumentCommand.kt +++ b/kover-cli/src/main/kotlin/kotlinx/kover/cli/commands/OfflineInstrumentCommand.kt @@ -16,9 +16,8 @@ package kotlinx.kover.cli.commands -import com.intellij.rt.coverage.instrument.api.OfflineInstrumentationApi -import com.intellij.rt.coverage.report.api.Filters -import kotlinx.kover.cli.util.asPatterns +import kotlinx.kover.cli.util.asRegex +import kotlinx.kover.features.java.KoverLegacyFeatures import org.kohsuke.args4j.Argument import org.kohsuke.args4j.Option import java.io.File @@ -63,21 +62,14 @@ internal class OfflineInstrumentCommand : Command { override fun call(output: PrintWriter, errorWriter: PrintWriter): Int { - // disable ConDy for offline instrumentations - System.setProperty("coverage.condy.enable", "false") - - val outputRoots = ArrayList(roots.size) - for (i in roots.indices) { - outputRoots.add(outputDir!!) - } - val filters = Filters( - includeClasses.asPatterns(), - excludeClasses.asPatterns(), - excludeAnnotation.asPatterns() + val filters = KoverLegacyFeatures.ClassFilters( + includeClasses.asRegex().toSet(), + excludeClasses.asRegex().toSet(), + excludeAnnotation.asRegex().toSet() ) try { - OfflineInstrumentationApi.instrument(roots, outputRoots, filters, countHits) + KoverLegacyFeatures.instrument(outputDir!!, roots, filters, countHits) } catch (e: Exception) { errorWriter.println("Instrumentation failed: " + e.message) return -1 diff --git a/kover-cli/src/main/kotlin/kotlinx/kover/cli/commands/ReportCommand.kt b/kover-cli/src/main/kotlin/kotlinx/kover/cli/commands/ReportCommand.kt index 4ba34eea..0262e66e 100644 --- a/kover-cli/src/main/kotlin/kotlinx/kover/cli/commands/ReportCommand.kt +++ b/kover-cli/src/main/kotlin/kotlinx/kover/cli/commands/ReportCommand.kt @@ -16,9 +16,9 @@ package kotlinx.kover.cli.commands -import com.intellij.rt.coverage.report.api.Filters -import com.intellij.rt.coverage.report.api.ReportApi -import kotlinx.kover.cli.util.asPatterns +import kotlinx.kover.cli.util.asRegex +import kotlinx.kover.features.jvm.KoverLegacyFeatures +import kotlinx.kover.features.jvm.KoverLegacyFeatures.ClassFilters import org.kohsuke.args4j.Argument import org.kohsuke.args4j.Option import java.io.File @@ -77,15 +77,16 @@ internal class ReportCommand : Command { override fun call(output: PrintWriter, errorWriter: PrintWriter): Int { - val filters = Filters( - includeClasses.asPatterns(), - excludeClasses.asPatterns(), - excludeAnnotation.asPatterns() + val filters = ClassFilters( + includeClasses.asRegex().toSet(), + excludeClasses.asRegex().toSet(), + excludeAnnotation.asRegex().toSet() ) + var fail = false if (xmlFile != null) { try { - ReportApi.xmlReport(xmlFile, title ?: "Kover XML Report", binaryReports, outputRoots, sourceRoots, filters) + KoverLegacyFeatures.xmlReport(xmlFile, binaryReports, outputRoots, sourceRoots, title ?: "Kover XML Report", filters) } catch (e: IOException) { fail = true errorWriter.println("XML generation failed: " + e.message) @@ -93,7 +94,7 @@ internal class ReportCommand : Command { } if (htmlDir != null) { try { - ReportApi.htmlReport(htmlDir, title, null, binaryReports, outputRoots, sourceRoots, filters) + KoverLegacyFeatures.htmlReport(htmlDir, binaryReports, outputRoots, sourceRoots, title ?: "Kover HTML Report", filters) } catch (e: IOException) { fail = true errorWriter.println("HTML generation failed: " + e.message) diff --git a/kover-cli/src/main/kotlin/kotlinx/kover/cli/util/KoverUtils.kt b/kover-cli/src/main/kotlin/kotlinx/kover/cli/util/KoverUtils.kt index f0445677..e0e01070 100644 --- a/kover-cli/src/main/kotlin/kotlinx/kover/cli/util/KoverUtils.kt +++ b/kover-cli/src/main/kotlin/kotlinx/kover/cli/util/KoverUtils.kt @@ -16,9 +16,7 @@ package kotlinx.kover.cli.util -import java.util.regex.Pattern - -internal fun List.asPatterns(): List = map { Pattern.compile(it.wildcardsToRegex()) } +internal fun List.asRegex(): List = map { it.wildcardsToRegex() } /** * Replaces characters `*` or `.` to `.*` and `.` regexp characters and also add escape char '\' before regexp metacharacters (see [regexMetacharactersSet]). diff --git a/kover-features-java/build.gradle.kts b/kover-features-java/build.gradle.kts new file mode 100644 index 00000000..a6ba8f22 --- /dev/null +++ b/kover-features-java/build.gradle.kts @@ -0,0 +1,46 @@ +/* + * Copyright 2000-2024 JetBrains s.r.o. + * + * 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. + */ + +plugins { + java + id("kover-publishing-conventions") +} + +extensions.configure { + description.set("Implementation of calling the main features of Kover via Java invokes") + fatJar.set(true) +} + +java { + sourceCompatibility = JavaVersion.VERSION_1_7 + targetCompatibility = JavaVersion.VERSION_1_7 +} + +repositories { + mavenCentral() +} + +tasks.processResources { + filesMatching("**/kover.version") { + filter { + it.replace("\$version", project.version.toString()) + } + } +} + +dependencies { + implementation(libs.intellij.reporter) +} diff --git a/kover-features-java/src/main/java/kotlinx/kover/features/java/ConDySettings.java b/kover-features-java/src/main/java/kotlinx/kover/features/java/ConDySettings.java new file mode 100644 index 00000000..02f429b2 --- /dev/null +++ b/kover-features-java/src/main/java/kotlinx/kover/features/java/ConDySettings.java @@ -0,0 +1,36 @@ +package kotlinx.kover.features.java; + +/** + * Internal class to control JVM ConDy settings. + */ +final class ConDySettings { + + private ConDySettings() { + // no-op + } + + private static final String CONDY_SYSTEM_PARAM_NAME = "coverage.condy.enable"; + + /** + * Disable JVM ConDy during instrumentation. + * + * @return previous value of ConDy setting + */ + static String disableConDy() { + // disable ConDy for offline instrumentations + return System.setProperty(CONDY_SYSTEM_PARAM_NAME, "false"); + } + + /** + * Restore previous value of JVM ConDy setting. + * + * @param prevValue new setting value + */ + static void restoreConDy(String prevValue) { + if (prevValue == null) { + System.clearProperty(CONDY_SYSTEM_PARAM_NAME); + } else { + System.setProperty(CONDY_SYSTEM_PARAM_NAME, prevValue); + } + } +} diff --git a/kover-features-java/src/main/java/kotlinx/kover/features/java/KoverFeatures.java b/kover-features-java/src/main/java/kotlinx/kover/features/java/KoverFeatures.java new file mode 100644 index 00000000..28462f7b --- /dev/null +++ b/kover-features-java/src/main/java/kotlinx/kover/features/java/KoverFeatures.java @@ -0,0 +1,42 @@ +package kotlinx.kover.features.java; + +import java.io.InputStream; +import java.util.Scanner; + +/** + * A class for using features via Java calls. + */ +public class KoverFeatures { + private static final String koverVersion = readVersion(); + + /** + * Getting the Kover version. + * + * @return The version of Kover used in these utilities. + */ + public static String getVersion() { + return koverVersion; + } + + /** + * Create instance to instrument already compiled class-files. + * + * @return instrumenter for offline instrumentation. + */ + public static OfflineInstrumenter createOfflineInstrumenter() { + return new OfflineInstrumenterImpl(false); + } + + private static String readVersion() { + String version = "unrecognized"; + // read version from file in resources + try (InputStream stream = KoverFeatures.class.getClassLoader().getResourceAsStream("kover.version")) { + if (stream != null) { + version = new Scanner(stream).nextLine(); + } + } catch (Throwable e) { + // can't read + } + return version; + } +} diff --git a/kover-features-java/src/main/java/kotlinx/kover/features/java/KoverLegacyFeatures.java b/kover-features-java/src/main/java/kotlinx/kover/features/java/KoverLegacyFeatures.java new file mode 100644 index 00000000..bfc540ae --- /dev/null +++ b/kover-features-java/src/main/java/kotlinx/kover/features/java/KoverLegacyFeatures.java @@ -0,0 +1,134 @@ +package kotlinx.kover.features.java; + +import com.intellij.rt.coverage.instrument.api.OfflineInstrumentationApi; +import com.intellij.rt.coverage.report.api.Filters; +import com.intellij.rt.coverage.report.api.ReportApi; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import java.util.regex.Pattern; + +/** + * Kover Features for support Kover capabilities in Kover CLI via outdated API. + */ +public class KoverLegacyFeatures { + + /** + * Generate modified class-files to measure the coverage. + * + * @param resultDir Directory where the instrumented class-files will be placed + * @param originalDirs Root directories where the original files are located, the coverage of which needs to be measured + * @param filters Filters to limit the classes that will be displayed in the report + * @param countHits Flag indicating whether to count the number of executions to each block of code. {@code false} if it is enough to register only the fact of at least one execution + */ + public static void instrument(File resultDir, + List originalDirs, + ClassFilters filters, + boolean countHits + ) { + ArrayList outputs = new ArrayList<>(originalDirs.size()); + for (int i = 0; i < originalDirs.size(); i++) { + outputs.add(resultDir); + } + + String condySetting = ConDySettings.disableConDy(); + try { + OfflineInstrumentationApi.instrument(originalDirs, outputs, convertFilters(filters), countHits); + } finally { + ConDySettings.restoreConDy(condySetting); + } + } + + /** + * Generate Kover XML report, compatible with JaCoCo XML. + * + * @param xmlFile path to the generated XML report + * @param binaryReports list of coverage binary reports in IC format + * @param classfileDirs list of root directories for compiled class-files + * @param sourceDirs list of root directories for Java and Kotlin source files + * @param title Title for header + * @param filters Filters to limit the classes that will be displayed in the report + * @throws IOException In case of a report generation error + */ + public static void xmlReport( + File xmlFile, + List binaryReports, + List classfileDirs, + List sourceDirs, + String title, + ClassFilters filters + ) throws IOException { + ReportApi.xmlReport(xmlFile, title, binaryReports, classfileDirs, sourceDirs, convertFilters(filters)); + } + + /** + * Generate Kover HTML report. + * + * @param htmlDir output directory with result HTML report + * @param binaryReports list of coverage binary reports in IC format + * @param classfileDirs list of root directories for compiled class-files + * @param sourceDirs list of root directories for Java and Kotlin source files + * @param title Title for header + * @param filters Filters to limit the classes that will be displayed in the report. + * @throws IOException In case of a report generation error + */ + public static void htmlReport( + File htmlDir, + List binaryReports, + List classfileDirs, + List sourceDirs, + String title, + ClassFilters filters + ) throws IOException { + ReportApi.htmlReport(htmlDir, title, null, binaryReports, classfileDirs, sourceDirs, convertFilters(filters)); + } + + /** + * Class filters. + */ + public static class ClassFilters { + /** + * If specified, only the classes specified in this field are filtered. + */ + public final Set includeClasses; + + /** + * The classes specified in this field are not filtered. + */ + public final Set excludeClasses; + + /** + * Classes that have at least one of the annotations specified in this field are not filtered. + */ + public final Set excludeAnnotation; + + public ClassFilters(Set includeClasses, + Set excludeClasses, + Set excludeAnnotation) { + this.includeClasses = includeClasses; + this.excludeClasses = excludeClasses; + this.excludeAnnotation = excludeAnnotation; + } + } + + private static Filters convertFilters(ClassFilters filters) { + return new Filters( + convert(filters.includeClasses), + convert(filters.excludeClasses), + convert(filters.excludeAnnotation) + ); + } + + private static List convert(Set regexes) { + ArrayList patterns = new ArrayList<>(regexes.size()); + for (String regex : regexes) { + patterns.add(Pattern.compile(regex)); + } + return patterns; + } + + +} diff --git a/kover-features-java/src/main/java/kotlinx/kover/features/java/OfflineInstrumenter.java b/kover-features-java/src/main/java/kotlinx/kover/features/java/OfflineInstrumenter.java new file mode 100644 index 00000000..a8829144 --- /dev/null +++ b/kover-features-java/src/main/java/kotlinx/kover/features/java/OfflineInstrumenter.java @@ -0,0 +1,19 @@ +package kotlinx.kover.features.java; + +import java.io.IOException; +import java.io.InputStream; + +/** + * Class for instrumentation of JVM byte code of already compiled class-files. + */ +public interface OfflineInstrumenter { + /** + * Modify byte code of single class-file to measure the coverage of this class. + * + * @param originalClass input stream with byte code of original class-file + * @param debugName name of the class or class-file, which is used in the error message + * @return instrumented byte code + * @throws IOException in case of any instrumentation error + */ + byte[] instrument(InputStream originalClass, String debugName) throws IOException; +} \ No newline at end of file diff --git a/kover-features-java/src/main/java/kotlinx/kover/features/java/OfflineInstrumenterImpl.java b/kover-features-java/src/main/java/kotlinx/kover/features/java/OfflineInstrumenterImpl.java new file mode 100644 index 00000000..7a5f4076 --- /dev/null +++ b/kover-features-java/src/main/java/kotlinx/kover/features/java/OfflineInstrumenterImpl.java @@ -0,0 +1,34 @@ +package kotlinx.kover.features.java; + +import com.intellij.rt.coverage.instrument.api.OfflineInstrumentationApi; + +import java.io.IOException; +import java.io.InputStream; + +/** + * Implementation of {@link OfflineInstrumenter}. + * The class should not be explicitly used from the outside. + */ +class OfflineInstrumenterImpl implements OfflineInstrumenter { + private final boolean countHits; + + OfflineInstrumenterImpl(boolean countHits) { + this.countHits = countHits; + } + + @Override + public byte[] instrument(InputStream originalClass, String debugName) throws IOException { + String condySetting = ConDySettings.disableConDy(); + + try { + return OfflineInstrumentationApi.instrument(originalClass, countHits); + } catch (Throwable e) { + throw new IOException( + String.format("Error while instrumenting '%s' with Kover instrumenter version '%s'", + debugName, KoverFeatures.getVersion()), e); + } finally { + ConDySettings.restoreConDy(condySetting); + } + } + +} diff --git a/kover-features-java/src/main/resources/kover.version b/kover-features-java/src/main/resources/kover.version new file mode 100644 index 00000000..03f59033 --- /dev/null +++ b/kover-features-java/src/main/resources/kover.version @@ -0,0 +1 @@ +$version \ No newline at end of file diff --git a/kover-offline-runtime/build.gradle.kts b/kover-offline-runtime/build.gradle.kts index d8873744..74638369 100644 --- a/kover-offline-runtime/build.gradle.kts +++ b/kover-offline-runtime/build.gradle.kts @@ -33,13 +33,17 @@ repositories { mavenCentral() } +val fatJarDependency = "fatJar" +val fatJarConfiguration = configurations.create(fatJarDependency) + dependencies { - implementation(libs.intellij.offline) + compileOnly(libs.intellij.offline) + fatJarConfiguration(libs.intellij.offline) } tasks.jar { from( - configurations.runtimeClasspath.get().map { if (it.isDirectory) it else zipTree(it) } + fatJarConfiguration.map { if (it.isDirectory) it else zipTree(it) } ) { exclude("OSGI-OPT/**") exclude("META-INF/**") diff --git a/kover-offline-runtime/src/main/java/kotlinx/kover/offline/runtime/KoverInit.java b/kover-offline-runtime/src/main/java/kotlinx/kover/offline/runtime/KoverInit.java index d387c719..6f569933 100644 --- a/kover-offline-runtime/src/main/java/kotlinx/kover/offline/runtime/KoverInit.java +++ b/kover-offline-runtime/src/main/java/kotlinx/kover/offline/runtime/KoverInit.java @@ -1,6 +1,7 @@ package kotlinx.kover.offline.runtime; import com.intellij.rt.coverage.offline.api.CoverageRuntime; +import com.intellij.rt.coverage.util.ErrorReporter; import kotlinx.kover.offline.runtime.api.KoverRuntime; import java.io.File; @@ -17,16 +18,21 @@ class KoverInit { static { - String reportNameSavedOnExitProp = System.getProperty(REPORT_PROPERTY_NAME); String logFileProp = System.getProperty(LOG_FILE_PROPERTY_NAME); - - if (logFileProp != null) { - CoverageRuntime.setLogPath(new File(LOG_FILE_PROPERTY_NAME)); + if (logFileProp == null) { + // by default, we do not create a file, because we do not know what rights our application is running with + // and whether it can create files in the current directory or next to the binary report file + CoverageRuntime.setLogPath(null); } else { - CoverageRuntime.setLogPath(new File(KoverRuntime.DEFAULT_LOG_FILE_NAME)); + CoverageRuntime.setLogPath(new File(logFileProp)); } + // setting the logging level in the "standard" error output stream + CoverageRuntime.setLogLevel(ErrorReporter.WARNING); + + String reportNameSavedOnExitProp = System.getProperty(REPORT_PROPERTY_NAME); if (reportNameSavedOnExitProp != null) { + // if a parameter is passed, then use the shutdown hook to save the binary report to a file saveOnExit(reportNameSavedOnExitProp); } } diff --git a/kover-offline-runtime/src/main/java/kotlinx/kover/offline/runtime/api/KoverRuntime.java b/kover-offline-runtime/src/main/java/kotlinx/kover/offline/runtime/api/KoverRuntime.java index c89c9db1..25031c1c 100644 --- a/kover-offline-runtime/src/main/java/kotlinx/kover/offline/runtime/api/KoverRuntime.java +++ b/kover-offline-runtime/src/main/java/kotlinx/kover/offline/runtime/api/KoverRuntime.java @@ -16,13 +16,6 @@ */ public class KoverRuntime { - /** - * Default name of file with Kover offline logs. - * - * Can be overridden using the {@link KoverRuntime#LOG_FILE_PROPERTY_NAME} property. - */ - public static final String DEFAULT_LOG_FILE_NAME = "kover-offline.log"; - /** * JVM property name used to define the path where the offline report will be stored. *

@@ -37,7 +30,7 @@ public class KoverRuntime { * JVM property name used to define the path to the file with Kover offline logs. * *

- * If this property is not specified, the logs are saved to the {@link KoverRuntime#DEFAULT_LOG_FILE_NAME} file located in the current directory. + * If this property is not specified, the logs file will not be created. */ public static final String LOG_FILE_PROPERTY_NAME = "kover.offline.log.file.path"; diff --git a/settings.gradle.kts b/settings.gradle.kts index 6001d9a9..06e794ae 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -14,6 +14,7 @@ dependencyResolutionManagement { } } +include(":kover-features-java") include(":kover-gradle-plugin") include(":kover-cli") include(":kover-offline-runtime") From 0076e9962b46f14cc91ff56b66b3b636e07fd759 Mon Sep 17 00:00:00 2001 From: Sergey Shanshin Date: Thu, 15 Feb 2024 16:08:57 +0100 Subject: [PATCH 2/3] Apply suggestions from code review Co-authored-by: Leonid Startsev --- docs/offline-instrumentation/index.md | 8 ++++---- kover-features-java/build.gradle.kts | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/offline-instrumentation/index.md b/docs/offline-instrumentation/index.md index 8ad66488..0dda731d 100644 --- a/docs/offline-instrumentation/index.md +++ b/docs/offline-instrumentation/index.md @@ -18,11 +18,11 @@ For instrumentation, you must first build the application, then the root directo must be passed to Kover CLI as arguments, see [Kover CLI](../cli#offline-instrumentation) for the technical details. #### Instrumentation by Kover Features -Kover Features is an artifact with Java classes that provide capabilities of the Kover library. +Kover Features is a library that provides capabilities similar to Kover CLI and Kover Gradle plugin. -Dependency on Kover Features `org.jetbrains.kotlinx:kover-features-java:0.7.5`. +You can declare a dependency on Kover Features using following coordinates: `org.jetbrains.kotlinx:kover-features-java:0.7.5`. -Then you need to use the Kover Features classes to instrument the bytecode of each class: +Then you can use the Kover Features classes to instrument the bytecode of each class: ```kotlin import kotlinx.kover.features.java.KoverFeatures // ... @@ -36,7 +36,7 @@ import kotlinx.kover.features.java.KoverFeatures ### Dump coverage result -To run classes instrumented offline, you'll need to add `org.jetbrains.kotlinx:kover-offline-runtime` artifact to the application's classpath. +To run classes instrumented offline (with CLI) or programmatically (with Kover Features), you'll need to add `org.jetbrains.kotlinx:kover-offline-runtime` artifact to the application's classpath. There are several ways to get coverage: diff --git a/kover-features-java/build.gradle.kts b/kover-features-java/build.gradle.kts index a6ba8f22..6177bd9d 100644 --- a/kover-features-java/build.gradle.kts +++ b/kover-features-java/build.gradle.kts @@ -20,7 +20,7 @@ plugins { } extensions.configure { - description.set("Implementation of calling the main features of Kover via Java invokes") + description.set("Implementation of calling the main features of Kover programmatically") fatJar.set(true) } From a44eb4d94465d87b07d7a9203b311342936dcf5a Mon Sep 17 00:00:00 2001 From: "Sergey.Shanshin" Date: Thu, 15 Feb 2024 16:13:53 +0100 Subject: [PATCH 3/3] ~review fixes --- docs/offline-instrumentation/index.md | 4 ++-- kover-cli/build.gradle.kts | 2 +- .../kover/cli/commands/OfflineInstrumentCommand.kt | 2 +- .../kotlin/kotlinx/kover/cli/commands/ReportCommand.kt | 4 ++-- .../build.gradle.kts | 0 .../kotlinx/kover/features/jvm}/ConDySettings.java | 2 +- .../kotlinx/kover/features/jvm}/KoverFeatures.java | 2 +- .../kover/features/jvm}/KoverLegacyFeatures.java | 10 +++++----- .../kover/features/jvm}/OfflineInstrumenter.java | 2 +- .../kover/features/jvm}/OfflineInstrumenterImpl.java | 6 +++--- .../src/main/resources/kover.version | 0 settings.gradle.kts | 2 +- 12 files changed, 18 insertions(+), 18 deletions(-) rename {kover-features-java => kover-features-jvm}/build.gradle.kts (100%) rename {kover-features-java/src/main/java/kotlinx/kover/features/java => kover-features-jvm/src/main/java/kotlinx/kover/features/jvm}/ConDySettings.java (95%) rename {kover-features-java/src/main/java/kotlinx/kover/features/java => kover-features-jvm/src/main/java/kotlinx/kover/features/jvm}/KoverFeatures.java (96%) rename {kover-features-java/src/main/java/kotlinx/kover/features/java => kover-features-jvm/src/main/java/kotlinx/kover/features/jvm}/KoverLegacyFeatures.java (95%) rename {kover-features-java/src/main/java/kotlinx/kover/features/java => kover-features-jvm/src/main/java/kotlinx/kover/features/jvm}/OfflineInstrumenter.java (94%) rename {kover-features-java/src/main/java/kotlinx/kover/features/java => kover-features-jvm/src/main/java/kotlinx/kover/features/jvm}/OfflineInstrumenterImpl.java (84%) rename {kover-features-java => kover-features-jvm}/src/main/resources/kover.version (100%) diff --git a/docs/offline-instrumentation/index.md b/docs/offline-instrumentation/index.md index 0dda731d..c0caf689 100644 --- a/docs/offline-instrumentation/index.md +++ b/docs/offline-instrumentation/index.md @@ -20,11 +20,11 @@ must be passed to Kover CLI as arguments, see [Kover CLI](../cli#offline-instrum #### Instrumentation by Kover Features Kover Features is a library that provides capabilities similar to Kover CLI and Kover Gradle plugin. -You can declare a dependency on Kover Features using following coordinates: `org.jetbrains.kotlinx:kover-features-java:0.7.5`. +You can declare a dependency on Kover Features using following coordinates: `org.jetbrains.kotlinx:kover-features-jvm:0.7.5`. Then you can use the Kover Features classes to instrument the bytecode of each class: ```kotlin -import kotlinx.kover.features.java.KoverFeatures +import kotlinx.kover.features.jvm.KoverFeatures // ... val instrumenter = KoverFeatures.createOfflineInstrumenter() diff --git a/kover-cli/build.gradle.kts b/kover-cli/build.gradle.kts index 1f33a8e5..cf245634 100644 --- a/kover-cli/build.gradle.kts +++ b/kover-cli/build.gradle.kts @@ -33,7 +33,7 @@ kotlin { } dependencies { - implementation(project(":kover-features-java")) + implementation(project(":kover-features-jvm")) implementation(libs.args4j) diff --git a/kover-cli/src/main/kotlin/kotlinx/kover/cli/commands/OfflineInstrumentCommand.kt b/kover-cli/src/main/kotlin/kotlinx/kover/cli/commands/OfflineInstrumentCommand.kt index 6ee71d72..247944b6 100644 --- a/kover-cli/src/main/kotlin/kotlinx/kover/cli/commands/OfflineInstrumentCommand.kt +++ b/kover-cli/src/main/kotlin/kotlinx/kover/cli/commands/OfflineInstrumentCommand.kt @@ -17,7 +17,7 @@ package kotlinx.kover.cli.commands import kotlinx.kover.cli.util.asRegex -import kotlinx.kover.features.java.KoverLegacyFeatures +import kotlinx.kover.features.jvm.KoverLegacyFeatures import org.kohsuke.args4j.Argument import org.kohsuke.args4j.Option import java.io.File diff --git a/kover-cli/src/main/kotlin/kotlinx/kover/cli/commands/ReportCommand.kt b/kover-cli/src/main/kotlin/kotlinx/kover/cli/commands/ReportCommand.kt index 0262e66e..fa0eba6c 100644 --- a/kover-cli/src/main/kotlin/kotlinx/kover/cli/commands/ReportCommand.kt +++ b/kover-cli/src/main/kotlin/kotlinx/kover/cli/commands/ReportCommand.kt @@ -86,7 +86,7 @@ internal class ReportCommand : Command { var fail = false if (xmlFile != null) { try { - KoverLegacyFeatures.xmlReport(xmlFile, binaryReports, outputRoots, sourceRoots, title ?: "Kover XML Report", filters) + KoverLegacyFeatures.generateXmlReport(xmlFile, binaryReports, outputRoots, sourceRoots, title ?: "Kover XML Report", filters) } catch (e: IOException) { fail = true errorWriter.println("XML generation failed: " + e.message) @@ -94,7 +94,7 @@ internal class ReportCommand : Command { } if (htmlDir != null) { try { - KoverLegacyFeatures.htmlReport(htmlDir, binaryReports, outputRoots, sourceRoots, title ?: "Kover HTML Report", filters) + KoverLegacyFeatures.generateHtmlReport(htmlDir, binaryReports, outputRoots, sourceRoots, title ?: "Kover HTML Report", filters) } catch (e: IOException) { fail = true errorWriter.println("HTML generation failed: " + e.message) diff --git a/kover-features-java/build.gradle.kts b/kover-features-jvm/build.gradle.kts similarity index 100% rename from kover-features-java/build.gradle.kts rename to kover-features-jvm/build.gradle.kts diff --git a/kover-features-java/src/main/java/kotlinx/kover/features/java/ConDySettings.java b/kover-features-jvm/src/main/java/kotlinx/kover/features/jvm/ConDySettings.java similarity index 95% rename from kover-features-java/src/main/java/kotlinx/kover/features/java/ConDySettings.java rename to kover-features-jvm/src/main/java/kotlinx/kover/features/jvm/ConDySettings.java index 02f429b2..3b913275 100644 --- a/kover-features-java/src/main/java/kotlinx/kover/features/java/ConDySettings.java +++ b/kover-features-jvm/src/main/java/kotlinx/kover/features/jvm/ConDySettings.java @@ -1,4 +1,4 @@ -package kotlinx.kover.features.java; +package kotlinx.kover.features.jvm; /** * Internal class to control JVM ConDy settings. diff --git a/kover-features-java/src/main/java/kotlinx/kover/features/java/KoverFeatures.java b/kover-features-jvm/src/main/java/kotlinx/kover/features/jvm/KoverFeatures.java similarity index 96% rename from kover-features-java/src/main/java/kotlinx/kover/features/java/KoverFeatures.java rename to kover-features-jvm/src/main/java/kotlinx/kover/features/jvm/KoverFeatures.java index 28462f7b..04ba5dde 100644 --- a/kover-features-java/src/main/java/kotlinx/kover/features/java/KoverFeatures.java +++ b/kover-features-jvm/src/main/java/kotlinx/kover/features/jvm/KoverFeatures.java @@ -1,4 +1,4 @@ -package kotlinx.kover.features.java; +package kotlinx.kover.features.jvm; import java.io.InputStream; import java.util.Scanner; diff --git a/kover-features-java/src/main/java/kotlinx/kover/features/java/KoverLegacyFeatures.java b/kover-features-jvm/src/main/java/kotlinx/kover/features/jvm/KoverLegacyFeatures.java similarity index 95% rename from kover-features-java/src/main/java/kotlinx/kover/features/java/KoverLegacyFeatures.java rename to kover-features-jvm/src/main/java/kotlinx/kover/features/jvm/KoverLegacyFeatures.java index bfc540ae..8c091c53 100644 --- a/kover-features-java/src/main/java/kotlinx/kover/features/java/KoverLegacyFeatures.java +++ b/kover-features-jvm/src/main/java/kotlinx/kover/features/jvm/KoverLegacyFeatures.java @@ -1,4 +1,4 @@ -package kotlinx.kover.features.java; +package kotlinx.kover.features.jvm; import com.intellij.rt.coverage.instrument.api.OfflineInstrumentationApi; import com.intellij.rt.coverage.report.api.Filters; @@ -34,11 +34,11 @@ public static void instrument(File resultDir, outputs.add(resultDir); } - String condySetting = ConDySettings.disableConDy(); + String previousConDySetting = ConDySettings.disableConDy(); try { OfflineInstrumentationApi.instrument(originalDirs, outputs, convertFilters(filters), countHits); } finally { - ConDySettings.restoreConDy(condySetting); + ConDySettings.restoreConDy(previousConDySetting); } } @@ -53,7 +53,7 @@ public static void instrument(File resultDir, * @param filters Filters to limit the classes that will be displayed in the report * @throws IOException In case of a report generation error */ - public static void xmlReport( + public static void generateXmlReport( File xmlFile, List binaryReports, List classfileDirs, @@ -75,7 +75,7 @@ public static void xmlReport( * @param filters Filters to limit the classes that will be displayed in the report. * @throws IOException In case of a report generation error */ - public static void htmlReport( + public static void generateHtmlReport( File htmlDir, List binaryReports, List classfileDirs, diff --git a/kover-features-java/src/main/java/kotlinx/kover/features/java/OfflineInstrumenter.java b/kover-features-jvm/src/main/java/kotlinx/kover/features/jvm/OfflineInstrumenter.java similarity index 94% rename from kover-features-java/src/main/java/kotlinx/kover/features/java/OfflineInstrumenter.java rename to kover-features-jvm/src/main/java/kotlinx/kover/features/jvm/OfflineInstrumenter.java index a8829144..411910ba 100644 --- a/kover-features-java/src/main/java/kotlinx/kover/features/java/OfflineInstrumenter.java +++ b/kover-features-jvm/src/main/java/kotlinx/kover/features/jvm/OfflineInstrumenter.java @@ -1,4 +1,4 @@ -package kotlinx.kover.features.java; +package kotlinx.kover.features.jvm; import java.io.IOException; import java.io.InputStream; diff --git a/kover-features-java/src/main/java/kotlinx/kover/features/java/OfflineInstrumenterImpl.java b/kover-features-jvm/src/main/java/kotlinx/kover/features/jvm/OfflineInstrumenterImpl.java similarity index 84% rename from kover-features-java/src/main/java/kotlinx/kover/features/java/OfflineInstrumenterImpl.java rename to kover-features-jvm/src/main/java/kotlinx/kover/features/jvm/OfflineInstrumenterImpl.java index 7a5f4076..b48583fc 100644 --- a/kover-features-java/src/main/java/kotlinx/kover/features/java/OfflineInstrumenterImpl.java +++ b/kover-features-jvm/src/main/java/kotlinx/kover/features/jvm/OfflineInstrumenterImpl.java @@ -1,4 +1,4 @@ -package kotlinx.kover.features.java; +package kotlinx.kover.features.jvm; import com.intellij.rt.coverage.instrument.api.OfflineInstrumentationApi; @@ -18,7 +18,7 @@ class OfflineInstrumenterImpl implements OfflineInstrumenter { @Override public byte[] instrument(InputStream originalClass, String debugName) throws IOException { - String condySetting = ConDySettings.disableConDy(); + String previousConDySetting = ConDySettings.disableConDy(); try { return OfflineInstrumentationApi.instrument(originalClass, countHits); @@ -27,7 +27,7 @@ public byte[] instrument(InputStream originalClass, String debugName) throws IOE String.format("Error while instrumenting '%s' with Kover instrumenter version '%s'", debugName, KoverFeatures.getVersion()), e); } finally { - ConDySettings.restoreConDy(condySetting); + ConDySettings.restoreConDy(previousConDySetting); } } diff --git a/kover-features-java/src/main/resources/kover.version b/kover-features-jvm/src/main/resources/kover.version similarity index 100% rename from kover-features-java/src/main/resources/kover.version rename to kover-features-jvm/src/main/resources/kover.version diff --git a/settings.gradle.kts b/settings.gradle.kts index 06e794ae..73afbc01 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -14,7 +14,7 @@ dependencyResolutionManagement { } } -include(":kover-features-java") +include(":kover-features-jvm") include(":kover-gradle-plugin") include(":kover-cli") include(":kover-offline-runtime")