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

How to use Kover CLI to merge binary reports? #731

Closed
mformetal opened this issue Jan 29, 2025 · 13 comments
Closed

How to use Kover CLI to merge binary reports? #731

mformetal opened this issue Jan 29, 2025 · 13 comments
Assignees
Labels
Question Support request issue type S: in progress Status: implementing or design in process

Comments

@mformetal
Copy link

We have 4 separate GHA jobs that generate coverage info.

  • One for running unit tests
  • 3 for running flavor-specific screenshot tests

I'm trying to use the Kover CLI to merge the binary reports from each of these jobs then create an XML report from that one to upload to our coverage reporting service.

In each job, I'm zipping the *.ic files and uploading them as an artifact, then after all these jobs complete, I'm running this script:

 # Unzip downloaded artifacts
          find ./ -name \*.zip -exec unzip {} \;
          
          # Create kover dir to write merged report to
          mkdir -p build/kover/merged
          
          # Find all coverage files 
          COVERAGE_FILES="$(find . -type f -name "*.ic" | cut -c 3-)"
          
          # Merge all ic files into one merged ic file
          java -jar kover/kover-cli-0.9.1.jar merge $COVERAGE_FILES --target build/kover/merged/koverMergedReport.ic
          
          ## Generate XML/HTML report from merged ic file
          
          # Get all src/ directories
          SRC_DIRS="$(find . -type d -name "src" | cut -c 3-)"
          
          java -jar kover/kover-cli-0.9.1.jar report build/kover/merged/koverMergedReport.ic --classfiles app --src $SRC_DIRS --xml build/kover/merged/koverMergedReport.xml --html build/kover/merged/html
          
          ls build/kover/merged
          cat build/kover/merged/koverMergedReport.xml

The ending cat is there for testing purposes. This always results in an empty XML file:

<?xml version="1.0" ?>
<report name="Kover XML Report">
<counter type="INSTRUCTION" missed="0" covered="0"/>
<counter type="BRANCH" missed="0" covered="0"/>
<counter type="LINE" missed="0" covered="0"/>
<counter type="METHOD" missed="0" covered="0"/>
<counter type="CLASS" missed="0" covered="0"/>
</report>

I'm for sure not passing the right --classfiles parameter, but what does this expect? From the documentation, I can't tell if it's supposed to be a combination of the generated build/**/classes

Additionally, the --src I'm passing seems to be wrong as well, as I get this warning:

[2025.01.29 09:43:24] (Coverage  WARN): Failed to check raw report file: java.io.FileNotFoundException: feature_flags/src (Is a directory)

The docs seem to indicate this should be a directory though.

Could I get some help in validating this is the right path forward using this tool?

@mformetal mformetal added Question Support request issue type S: untriaged Status: issue reported but unprocessed labels Jan 29, 2025
@shanshin
Copy link
Collaborator

shanshin commented Jan 29, 2025

Hi, empty reports generated when no class-files are found.

I'm for sure not passing the right --classfiles parameter, but what does this expect?

it should be a root directory with compiled class files, and not a project or a module root.
The specific compiler output directory depends on your project settings and project type.

For example for typical Gradle project with single JVM module it will be build/classes/kotlin/main if current directory is the root of this project.

Important thing that keys --classfiles and --src take only one argument, you should repeat it, like --src src/main/java --src src/kotlin/main or --classfiles build/classes/out --classfiles app/build/tmp/kotlin-classes/debug --classfiles app/build/tmp/kotlin-classes/release

@shanshin shanshin added S: in progress Status: implementing or design in process and removed S: untriaged Status: issue reported but unprocessed labels Jan 29, 2025
@mformetal
Copy link
Author

Hi, empty reports generated when no class-files are found.

I'm for sure not passing the right --classfiles parameter, but what does this expect?

it should be a root directory with compiled class files, and not a project or a module root. The specific compiler output directory depends on your project settings and project type.

For example for typical Gradle project with single JVM module it will be build/classes/kotlin/main if current directory is the root of this project.

Important thing that keys --classfiles and --src take only one argument, you should repeat it, like --src src/main/java --src src/kotlin/main or --classfiles build/classes/out --classfiles app/build/tmp/kotlin-classes/debug --classfiles app/build/tmp/kotlin-classes/release

Much appreciated - I'll add these to my script and get back to you with any further questions. Thanks!

@mformetal
Copy link
Author

Thanks again for your help here @shanshin - working through a few things and just getting back to this.

I followed your guidance and I'm passing the parameters to --src here

--src feature_flags/src/main/kotlin --src debug_menu/src/main/java...

and to --classfiles like so:

--classfiles jumio/build/intermediates/javac/scorebetDebug --classfiles jumio/build/tmp/kotlin-classes/scorebetDebug --classfiles feature_flags/build/intermediates/javac/scorebetDebug

And the binary reports I'm trying to merge ((These .ic files are generated from different runs and we need to rename them as to not have file collisions):

jumio/build/kover/bin-reports/testEspnDebugUnitTestScreenshot.ic jumio/build/kover/bin-reports/testHollywoodcasinoDebugUnitTestScreenshot.ic jumio/build/kover/bin-reports/testScorebetDebugUnitTest.ic jumio/build/kover/bin-reports/testScorebetDebugUnitTestScreenshot.ic feature_flags/build/kover/bin-reports/testEspnDebugUnitTestScreenshot.ic 

But the merged report seemingly only takes in the first entry:

<?xml version="1.0" ?>
<report name="Kover XML Report">
<package name="compileScorebetDebugJavaWithJavac/classes/bet/thescore/android/jumio">
<class name="compileScorebetDebugJavaWithJavac/classes/bet/thescore/android/jumio/BuildConfig" sourcefilename="BuildConfig.java">
<method name="&lt;clinit&gt;" desc="()V">
<counter type="INSTRUCTION" missed="6" covered="0"/>
<counter type="BRANCH" missed="0" covered="0"/>
<counter type="LINE" missed="2" covered="0"/>
</method>
<method name="&lt;init&gt;" desc="()V">
<counter type="INSTRUCTION" missed="2" covered="0"/>
<counter type="BRANCH" missed="0" covered="0"/>
<counter type="LINE" missed="1" covered="0"/>
</method>
<counter type="INSTRUCTION" missed="8" covered="0"/>
<counter type="BRANCH" missed="0" covered="0"/>
<counter type="LINE" missed="3" covered="0"/>
<counter type="METHOD" missed="2" covered="0"/>
</class>
<sourcefile name="BuildConfig.java">
<line nr="6" mi="2" ci="0" mb="0" cb="0"/>
<line nr="7" mi="3" ci="0" mb="0" cb="0"/>
<line nr="16" mi="3" ci="0" mb="0" cb="0"/>
<counter type="INSTRUCTION" missed="8" covered="0"/>
<counter type="BRANCH" missed="0" covered="0"/>
<counter type="LINE" missed="3" covered="0"/>
</sourcefile>
<counter type="INSTRUCTION" missed="8" covered="0"/>
<counter type="BRANCH" missed="0" covered="0"/>
<counter type="LINE" missed="3" covered="0"/>
<counter type="METHOD" missed="2" covered="0"/>
<counter type="CLASS" missed="1" covered="0"/>
</package>
<counter type="INSTRUCTION" missed="8" covered="0"/>
<counter type="BRANCH" missed="0" covered="0"/>
<counter type="LINE" missed="3" covered="0"/>
<counter type="METHOD" missed="2" covered="0"/>
<counter type="CLASS" missed="1" covered="0"/>
</report>

Any ideas on what I'm doing wrong here?

@shanshin
Copy link
Collaborator

shanshin commented Feb 3, 2025

If you merge all the ic files in advance, then there is no such problem?

@shanshin
Copy link
Collaborator

shanshin commented Feb 3, 2025

But the merged report seemingly only takes in the first entry:

Is there a problem that all coverage is 0 or that not all classes are present in the report?

@mformetal
Copy link
Author

If you merge all the ic files in advance, then there is no such problem?

I unfortunately have to merge .ic files in the manner I'm doing now, as we have multiple parallel jobs that produce them and we consume them in a final "after all tests are completed" job.

Is there a problem that all coverage is 0 or that not all classes are present in the report?

The coverage information in the report seems to be somewhat accurate for the classes that are present, but yes the main problem is that not all classes are present. I unfortunately don't know how to read the binary format, could it be a problem with this merging command?

COVERAGE_FILES="$(find . -type f -name "*.ic" | cut -c 3-)"
          
java -jar kover/kover-cli-0.9.1.jar merge $COVERAGE_FILES --target build/kover/merged/koverMergedReport.ic

And COVERAGE_FILES looks like:

jumio/build/kover/bin-reports/testEspnDebugUnitTestScreenshot.ic jumio/build/kover/bin-reports/testHollywoodcasinoDebugUnitTestScreenshot.ic jumio/build/kover/bin-reports/testScorebetDebugUnitTest.ic jumio/build/kover/bin-reports/testScorebetDebugUnitTestScreenshot.ic feature_flags/build/kover/bin-reports/testEspnDebugUnitTestScreenshot.ic feature_flags/build/kover/bin-reports/testHollywoodcasinoDebugUnitTestScreenshot.ic feature_flags/build/kover/bin-reports/testScorebetDebugUnitTest.ic feature_flags/build/kover/bin-reports/testScorebetDebugUnitTestScreenshot.ic debug_menu/build/kover/bin-reports/testEspnDebugUnitTestScreenshot.ic debug_menu/build/kover/bin-reports/testHollywoodcasinoDebugUnitTestScreenshot.ic debug_menu/build/kover/bin-reports/testScorebetDebugUnitTest.ic debug_menu/build/kover/bin-reports/testScorebetDebugUnitTestScreenshot.ic braintree/build/kover/bin-reports/testEspnDebugUnitTestScreenshot.ic....

@shanshin
Copy link
Collaborator

shanshin commented Feb 3, 2025

The coverage information in the report seems to be somewhat accurate for the classes that are present, but yes the main problem is that not all classes are present. I unfortunately don't know how to read the binary format, could it be a problem with this merging command?

The binary ic file does not specify which classes to include in the report, it only stores coverage data.
A class is included in the report only if there is a class-file in one of the paths passed using --classfiles, regardless of whether there is any information about this class in the ic report.

Therefore, you need to double-check the correctness of all the paths that were specified via --classfiles, and there must be class-files in these paths. In other words, a complete compilation of the entire code must take place on the machine where the report is generated.

@mformetal
Copy link
Author

The coverage information in the report seems to be somewhat accurate for the classes that are present, but yes the main problem is that not all classes are present. I unfortunately don't know how to read the binary format, could it be a problem with this merging command?

The binary ic file does not specify which classes to include in the report, it only stores coverage data. A class is included in the report only if there is a class-file in one of the paths passed using --classfiles, regardless of whether there is any information about this class in the ic report.

Therefore, you need to double-check the correctness of all the paths that were specified via --classfiles, and there must be class-files in these paths. In other words, a complete compilation of the entire code must take place on the machine where the report is generated.

I've verified that the paths specified by --classfiles have .class files in them, for example:

terminal -> find viewbinding/build/tmp/kotlin-classes/scorebetDebug 


viewbinding/build/tmp/kotlin-classes/scorebetDebug
viewbinding/build/tmp/kotlin-classes/scorebetDebug/META-INF
viewbinding/build/tmp/kotlin-classes/scorebetDebug/META-INF/viewbinding_scorebetDebug.kotlin_module
viewbinding/build/tmp/kotlin-classes/scorebetDebug/bet
viewbinding/build/tmp/kotlin-classes/scorebetDebug/bet/thescore
viewbinding/build/tmp/kotlin-classes/scorebetDebug/bet/thescore/android
viewbinding/build/tmp/kotlin-classes/scorebetDebug/bet/thescore/android/viewbinding
viewbinding/build/tmp/kotlin-classes/scorebetDebug/bet/thescore/android/viewbinding/BindingBottomSheetDialogFragment.class
viewbinding/build/tmp/kotlin-classes/scorebetDebug/bet/thescore/android/viewbinding/BindingFragment.class
viewbinding/build/tmp/kotlin-classes/scorebetDebug/bet/thescore/android/viewbinding/BindingActivity$onCreate$1.class
viewbinding/build/tmp/kotlin-classes/scorebetDebug/bet/thescore/android/viewbinding/BindingActivity.class
viewbinding/build/tmp/kotlin-classes/scorebetDebug/bet/thescore/android/viewbinding/ActivityViewBindingDelegateKt$viewBinding$1.class
viewbinding/build/tmp/kotlin-classes/scorebetDebug/bet/thescore/android/viewbinding/ViewBindingAliasesKt.class
viewbinding/build/tmp/kotlin-classes/scorebetDebug/bet/thescore/android/viewbinding/ActivityViewBindingDelegateKt.class
viewbinding/build/tmp/kotlin-classes/scorebetDebug/bet/thescore/android/viewbinding/BindingDialogFragment.class

And the --src directories are also valid:

terminal -> find viewbinding/src/main/kotlin 


viewbinding/src/main/kotlin
viewbinding/src/main/kotlin/bet
viewbinding/src/main/kotlin/bet/thescore
viewbinding/src/main/kotlin/bet/thescore/android
viewbinding/src/main/kotlin/bet/thescore/android/viewbinding
viewbinding/src/main/kotlin/bet/thescore/android/viewbinding/ViewBindingAliases.kt
viewbinding/src/main/kotlin/bet/thescore/android/viewbinding/BindingFragment.kt
viewbinding/src/main/kotlin/bet/thescore/android/viewbinding/ActivityViewBindingDelegate.kt
viewbinding/src/main/kotlin/bet/thescore/android/viewbinding/BindingActivity.kt
viewbinding/src/main/kotlin/bet/thescore/android/viewbinding/BindingDialogFragment.kt
viewbinding/src/main/kotlin/bet/thescore/android/viewbinding/BindingBottomSheetDialogFragment.kt

Should I be passing a directory to --classfiles or each individual file, i.e. --classfiles viewbinding/build/tmp/kotlin-classes/scorebetDebug or --classfiles iewbinding/build/tmp/kotlin-classes/scorebetDebug/bet/thescore/android/viewbinding/BindingBottomSheetDialogFragment.class? Same question for the --src parameter!

Sorry for the confusion and thanks for your help!

@shanshin
Copy link
Collaborator

shanshin commented Feb 3, 2025

Root directories for each type are expected, i.e. directories that do not include package names, e.g. --classfiles viewbinding/build/tmp/kotlin-classes/scorebetDebug and --src viewbinding/src/main/kotlin.

However, it is important that when calling CLI, the current directory points to the directory where viewbinding is located.

@mformetal
Copy link
Author

Root directories for each type are expected, i.e. directories that do not include package names, e.g. --classfiles viewbinding/build/tmp/kotlin-classes/scorebetDebug and --src viewbinding/src/main/kotlin.

However, it is important that when calling CLI, the current directory points to the directory where viewbinding is located.

So if I have a directory structure like:

root
---> viewbinding

I have to call the CLI from the viewbinding/ dir? Or I can call from root?

@shanshin
Copy link
Collaborator

shanshin commented Feb 3, 2025

By root directory for each type I meant the directory where the source files or class-files of a source set are placed.

For example, let's say you have such directories:

  • /tmp/my-project/viewbinding/build/tmp/kotlin-classes/scorebetDebug
  • /tmp/my-project/viewbinding/src/main/kotlin

In this case, you can specify --classfiles viewbinding/build/tmp/kotlin-classes/scorebetDebug and --src viewbinding/src/main/kotlin only when the current directory is /tmp/my-project when calling the CLI.

@mformetal
Copy link
Author

Got things working - thanks so much for your help!

@shanshin
Copy link
Collaborator

Closed as answered

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Question Support request issue type S: in progress Status: implementing or design in process
Projects
None yet
Development

No branches or pull requests

2 participants