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 can App module include kover(project(":lib")) for a large number of sub-modules? #594

Closed
trietbui85 opened this issue Apr 9, 2024 · 5 comments
Assignees
Labels
Question Support request issue type S: in progress Status: implementing or design in process

Comments

@trietbui85
Copy link

trietbui85 commented Apr 9, 2024

Describe what you would like to clarify about Kover
My project is multi modules, multi flavors, with the following structure

RootDir
- app  -> with 2 flavors: Dev & Google
- core
- - lib1 -> Kotlin module
- - lib2 -> Android module
- - ... and so on, might be up to 100 modules
- feature
- - feat1 -> Android module, depend on Core:lib1
- - feat2 -> Android module, depend on Core:lib1 & lib2
- - ... and so on, might be up to 100 modules

(My real project has more than 200 sub-modules)

App depends on all Feature modules, but not Core module. Therefore, its build.gradle with Kover block would be:

dependencies {
    implementation project(':feature:feat1')
    implementation project(':feature:feat2')
    // ... and so on, might be up to 100 modules 

    kover(project(":feature:feat1"))
    kover(project(":feature:feat2"))
    // ... and so on, might be up to 100 modules 
}

So, my question:

  1. Is there any less boilerplate to call kover(project(":feature:xxx"))? I don't think we should call it up to 100 times to include all Feature modules.
  2. In App level build.gradle, do I need to call kover(project(":core:xxx")) for Core modules? Or should it call kover(project(":core:xxx")) in the Feature module instead?
  3. What happens if I don't call kover(project(":feature:xxx")) in App module? I still see the coverage report, but is it the coverage of all modules in project, or just App module? 🤔
@trietbui85 trietbui85 added Question Support request issue type S: untriaged Status: issue reported but unprocessed labels Apr 9, 2024
@shanshin
Copy link
Collaborator

shanshin commented Apr 9, 2024

Hi,

  1. Is there any less boilerplate to call kover(project(":feature:xxx"))? I don't think we should call it up to 100 times to include all Feature modules.

you can use the functions to iterate through the projects in the build like subprojects or allprojects (depends on where you write it down).
e.g.

dependencies {
    allprojects {
        kover(this)
    }
}

*at the same time, it is important that the Kover plugin is applied in all projects that will be used in kover(this)

  1. In App level build.gradle, do I need to call kover(project(":core:xxx")) for Core modules? Or should it call kover(project(":core:xxx")) in the Feature module instead?

This approach is recommended: you select one specific project in which the task of generating the merged report will be located. We will call such a project a merging project.
In it, you must specify the kover dependencies (point above) for all projects whose source code should be included in the report (and in which test tasks should be launched).

For example, you decided that :app would be a merging project. You add dependencies to other projects using allprojects. To generate a report, you will need to call a task such as :app:koverHtmlReportDev. In this case, the report will contain classes from :app and all projects specified in the kover dependency.

  • it is important that in this case, the dev Android build variant is present in all projects (:app and kover dependencies).
  1. What happens if I don't call kover(project(":feature:xxx")) in App module? I still see the coverage report, but is it the coverage of all modules in project, or just App module? 🤔

if you do not specify the kover(project(":feature:xxx")) dependency, then classes from :feature:xxx will not be included in the merged report and you will see coverage only for classes from :app.


If your build consists of mixed Kotlin and Android projects, then I recommend trying to use the 0.8.0-Beta2 version of the Kover plugin.

@shanshin
Copy link
Collaborator

shanshin commented Apr 9, 2024

Such multi-project builds are very useful for obtaining use-cases of using the Kover plugin.
Could you please share your opinion, for your project, would it be convenient for you if the merged report (in :app project) included all the projects specified in the implementation(project(...)) dependency?

And so they were added recursively, e.g. : :app <- implementation(project(':feature:feat1')) <- implementation(project(':core:lib1')), thus, the merged report in :app would include classes from :feature:feat1 and :core:lib1

@woodii
Copy link

woodii commented May 14, 2024

hi @shanshin. Here is my feedback to your question above, since a have a similar usecase.

Could you please share your opinion, for your project, would it be convenient for you if the merged report (in :app project) included all the projects specified in the implementation(project(...)) dependency?

In my opinion this would be very useful. We currently need one merged report that we can pass to sonarqube. I can't think of a project where i would exclude modules which are implemented.

This feature would furthermore solve one problem of mine. We have a multi-module project which results in a few different apps. Depending on the buildvariant different modules are implemented. When using allprojects without any sort of filtering based on the buildvariant this results on too many modules in the report for that specific app. Furthermore the described idea using allprojects inside dependency block inside the app's build.gradle does not work for me.

I currently use the following workaround:

List<String> exclusions = List.of(":auth", ":core", ":features")
List<String> appAExclusions = List.of(":features:featureb")
List<String> appBExclusions = List.of(":features:featurea")

Boolean isA = buildVariant.toLowerCase().startsWith("appa")
List<String> applicableExclusions = generalExclusions + (isA ? appAExclusions : appBExclusions)

return rootProject.subprojects
    .findAll {
        !applicableExclusions.contains(it.buildTreePath)
    }.asList()

I then iterate over this list inside :app's dependency block and do kover(project)

Do you have any other suggestions which feel less like an hacky workaround? ;)

@shanshin shanshin added S: in progress Status: implementing or design in process and removed S: untriaged Status: issue reported but unprocessed labels Jun 27, 2024
@shanshin
Copy link
Collaborator

shanshin commented Jun 27, 2024

@trietbui85, @woodii, sorry for delayed answer, apparently, I missed the question.
Unfortunately, there is no good solution yet.

We are currently working on the potential transfer of code coverage measurement to the Kotlin Gradle Plugin.

So far, a prototype has been implemented, which is testing a different approach to collecting coverage.
It would be very useful for us to get a primary feedback on this plugin, what functionality is missing, what would you like to add (besides verification), if you have the opportunity to test the prototype.

You can apply the plugin by specifying in the settings.gradle[.kts] file

plugins {
    id("org.jetbrains.kotlinx.kover.aggregation") version "0.8.2"
}

There is no need to apply it anywhere else, all uses of the id("org.jetbrains.kotlinx.kover") plugin must be removed.

This aggregation plugin works differently.

Report generation tasks are created only in the root project, settings are made only in the settings.gradle.kts file.

To instrument the tests, you need to enable coverage measurement, this can be done by adding the CLI argument -Pkover, or by writing to settings.gradle.kts

kover {
    enableCoverage()

The key difference is that running the koverHtmlReport or koverXmlReport tasks does not automatically run the tests. Only those classes that were compiled within the same assembly will be included in the report, and coverage will be taken only from running tests.
This way you can by yourself control which coverage and which classes are included in the report - by starting different compilation and testing tasks.

In order not to restart tests in different builds, use the build cache.

@shanshin
Copy link
Collaborator

shanshin commented Sep 3, 2024

Closed as answered

@shanshin shanshin closed this as completed Sep 3, 2024
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

3 participants