Kotlin / kotlinx-kover

Apache License 2.0
1.25k stars 46 forks source link

Ability to create custom report variant for specific dependency project #584

Closed mgroth0 closed 2 days ago

mgroth0 commented 1 month ago

What is your use-case and why do you need this feature?

I would like to speed up my build by aggregating some tests into a single module. At the same time, I would like to retain the ability to check coverage of each module independently.

Imagine a project with 3 subprojects: :app, :core, and :lib. Let's say that the way things are now is that each subproject adds the kover plugin, and has its own tests. However, the overhead of running starting 3 separate test processes is too much. So, I want to only have one test test in :app, which will also test :core and :lib (3 projects is not significant overhead, but in real life there could be hundreds of subprojects).

I am looking at the newest API for creating custom variants in 8.0.0-Beta. I see in KoverVariantCreateConfig that I can create a new variant, however it doesn't seem that a variant can define its own dependencies. So here is the issue. Let's say :app adds kover depedencies on :core: and :lib. My understanding is that by default, all 3 modules code will be combined for the coverage verification rules. This means that you might be able to pass a rule even though you didn't cover a single line in :lib, because you covered enough lines in :core.

I was hoping custom variants solve this problem, but I cannot figure out how to do what I am imagining. I would like a custom variant in :app in which verification rules are only looking at a single kover dependency project, like :core.

Describe the solution you'd like

Possibly the closest thing I am finding looking at the API is KoverVariantSources.excludedSourceSets. If only there was something like KoverVariantSources.includedKoverDependencies: Set<String> in which each string was the path of a module. If empty, it could default to including all kover dependencies as well as the current project. but if we were to write includedKoverDependencies.append(':core') then that variant would analyze its rules only using the code fom :core, not even from the current project.

shanshin commented 1 month ago

Hi, could you clarify your use case?

Suppose you have :core and :lib projects, would you like to create a variant in which tests from both projects were run, but only classes from :lib were included in the report?

mgroth0 commented 1 month ago

Hey, sure I will clarify.

Let's say I just have :core and :lib.

I want to only have on test source set in :core. :lib will be on the classpath for the tests in :core, so I can add tests for :lib here as well.

For kover, lets say I want these two rules:

  1. cover at least 50% of the lines in :core
  2. cover at least 50% of the lines in :lib

If each module has its own test source set, this is easy. This is how my real life project works now.

When I move the tests from :lib to :core, I need to make sure that these verifications work in the same way. If I do no special configuration and just add :lib as a kover dependency for :core, then only :core will run koverVerify. And when :core runs koverVerify, it will not discriminate between code in :core and :lib. It will still check for the 50% threshold, but it won't care where the lines are. They might all be in one module or the other. And this is not the behavior that I want. Rather, I want to still verify that 50% of lines are covered for each indivual module, even though their tests are combined.

All of what I said above is my understanding on how it works, but I did not fully test this. It is somewhat of an assumption based on how I understood the documentation, so please let me know if I am misunderstanding.

shanshin commented 1 month ago

Please correct me if I made a mistake somewhere:

You want to verify coverage only for :lib subproject, but to do this, the tests from :core and :lib must be run

mgroth0 commented 1 month ago

Almost.

In my example, :lib has no test source set. All tests for :lib classes are contained in the :core test source set. The :core test source set also contains tests for :core classes.

When I execute :core:allTests, I am testing both :core and :lib.

When I execute :core:koverVerify I am verifing that the test source set of :core covered code from both :lib and :core.

The main issue is that I need verification rules to apply to each module separately. When Kover calculates whether or not 50% of the lines of ccode in :lib have been covered by :core tests, this calculation will need to completely ignore coverage of :core code.

At the same time, :core should have a separate verification rule that 50% of the lines of code in the :core main source set have been covered.

shanshin commented 1 month ago

It looks like project filters can be added:

kover {
    reports {
        variant("onlyForLib") {
            filters {
                includes {
                    projects.add(":lib")
                }
            }
            verify {
                rule {
                    // lib should be covered by 50%
                    minBound(50)
                }
            }
        }

        variant("onlyForCore") {
            filters {
                includes {
                    projects.add(":core")
                }
            }
            verify {
                rule {
                    // core should be covered by 60%
                    minBound(60)
                }
            }
        }
    }
}
mgroth0 commented 1 month ago

good idea. I think it would work!

shanshin commented 2 days ago

Implemented in 0.8.0