Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implemented Kover Features artifact to integration with Android Gradle Plugin #536

Merged
merged 3 commits into from
Feb 15, 2024
Merged
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
3 changes: 2 additions & 1 deletion docs/index.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
- [Kover Gradle Plugin](gradle-plugin)
- [Kover Command Line Interface](cli)
- [Kover Command Line Interface](cli)
- [Kover offline instrumentation](offline-instrumentation)
34 changes: 28 additions & 6 deletions docs/offline-instrumentation/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 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-jvm:0.7.5`.

Then you can use the Kover Features classes to instrument the bytecode of each class:
```kotlin
import kotlinx.kover.features.jvm.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 (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:

Expand Down Expand Up @@ -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
Expand Down
3 changes: 2 additions & 1 deletion kover-cli/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ kotlin {
}

dependencies {
implementation(libs.intellij.reporter)
implementation(project(":kover-features-jvm"))

implementation(libs.args4j)

testImplementation(kotlin("test"))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.jvm.KoverLegacyFeatures
import org.kohsuke.args4j.Argument
import org.kohsuke.args4j.Option
import java.io.File
Expand Down Expand Up @@ -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<File>(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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -77,23 +77,24 @@ 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.generateXmlReport(xmlFile, binaryReports, outputRoots, sourceRoots, title ?: "Kover XML Report", filters)
} catch (e: IOException) {
fail = true
errorWriter.println("XML generation failed: " + e.message)
}
}
if (htmlDir != null) {
try {
ReportApi.htmlReport(htmlDir, title, null, binaryReports, outputRoots, sourceRoots, filters)
KoverLegacyFeatures.generateHtmlReport(htmlDir, binaryReports, outputRoots, sourceRoots, title ?: "Kover HTML Report", filters)
} catch (e: IOException) {
fail = true
errorWriter.println("HTML generation failed: " + e.message)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,7 @@

package kotlinx.kover.cli.util

import java.util.regex.Pattern

internal fun List<String>.asPatterns(): List<Pattern> = map { Pattern.compile(it.wildcardsToRegex()) }
internal fun List<String>.asRegex(): List<String> = map { it.wildcardsToRegex() }

/**
* Replaces characters `*` or `.` to `.*` and `.` regexp characters and also add escape char '\' before regexp metacharacters (see [regexMetacharactersSet]).
Expand Down
46 changes: 46 additions & 0 deletions kover-features-jvm/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -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<Kover_publishing_conventions_gradle.KoverPublicationExtension> {
description.set("Implementation of calling the main features of Kover programmatically")
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)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package kotlinx.kover.features.jvm;

/**
* 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);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package kotlinx.kover.features.jvm;

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;
}
}
Loading