Kotlin / kotlinx-kover

Apache License 2.0
1.28k stars 48 forks source link

excludes still doesn't work #186

Closed NinoDLC closed 2 years ago

NinoDLC commented 2 years ago

build.gradle (project)

plugins {
  id 'org.jetbrains.kotlinx.kover' version "0.5.0"
}

build.gradle (android module)

android {
      testOptions {
        unitTests.all {
            if (it.name == "testDebugUnitTest") {
                kover {
                    disabled = false
                    excludes = [
                            "*_*Factory.*",
                            "*_Factory.*",
                            "Hilt_*",
                            "*_Hilt*",
                    ]
                }
            } else if (it.name == "testReleaseUnitTest") {
                kover {
                    disabled = true
                }
            }
        }
    }
}

In the report (generated by ./gradlew clean koverHtmlReport), no file is excluded: image

shanshin commented 2 years ago

Hi, in the current version of the API, the filter for instrumentation and the filter for reports are configuring in different places. In your example, you excluded a class from instrumentation - in this case, coverage for such a class will always be 0 in the report.

To exclude a class from a report, you just need to add an exclusion rule to the report generation task:

tasks.koverHtmlReport {
    excludes = [
        "*_*Factory.*",
        "*_Factory.*",
        "Hilt_*",
        "*_Hilt*",
        ]
}

In the upcoming releases, the API will be redesigned and it will be enough to specify the filter only in one place.

Duplicates #162

NinoDLC commented 2 years ago

I tried that too, I put that code in build.gradle (project), then ./gradlew clean koverHtmlReport, but nothing changed in the report.

tasks.koverHtmlReport {
    excludes = [
        "*_*Factory.*",
        "*_Factory.*",
        "Hilt_*",
        "*_Hilt*",
        ]
}
NinoDLC commented 2 years ago

You can check my gradle files (and / or generate the report) here : https://github.com/NinoDLC/Pokedexino

shanshin commented 2 years ago

You need to put the configuration

tasks.koverHtmlReport {
    excludes = [
            "*_*Factory.*",
            "*_Factory.*",
            "Hilt_*",
            "*_Hilt*",
    ]
}

in build.gradle module file Screenshot from 2022-05-31 13-59-41

Then, for example, the following report is generated for the package fr.delcey.pokedexino.ui.detail Screenshot from 2022-05-31 14-00-44

As far as I understand, the class fr.delcey.pokedexino.ui.detail.PokemonDetailViewModel_Factory should also be excluded from the report, for this it is necessary to remove the dot from the rule "*_Factory.*", because there is no dot at the end of the fully-qualified class name.

With

tasks.koverHtmlReport {
    excludes = [
            "*_*Factory*",
            "*_Factory*",
            "Hilt_*",
            "*_Hilt*",
    ]
}

report is Screenshot from 2022-05-31 14-10-01

NinoDLC commented 2 years ago

Thanks a lot ! Gradle is such a weird mess.

Still, I can't manage to exclude a file prefixed with Hilt_. As seen in the screenshot, Hilt_PokemonDetailFragment is still present even with the exclude pattern "Hilt_*".

If I use "**/Hilt_*", it doesn't work any better (it should, right ?). Interestingly, "*/Hilt_*" doesn't work either

If I use "*Hilt_*", it works, but this isn't a perfect solution.

Is it a bug or a (currently missing) feature ?

shanshin commented 2 years ago

Please note that the Kover works with fully-qualified class names.

Hilt_PokemonDetailFragment is a simple class name, fr.delcey.pokedexino.ui.detail.Hilt_PokemonDetailFragment is a fully-qualified name. Thus, if you want to exclude such a class, you need to write such a rule *.Hilt_* (so you exclude any class from non-root package with simple name started with Hilt_)

shanshin commented 2 years ago

No, it's possible to use only * and ? wildcards.

JaCoCo Gradle Plugin also supports only * and ? see, and. Therefore, in the Gradle , the ** is equivalent to *

NinoDLC commented 2 years ago

You mention fully-qualified class names, but is the type also somewhat considered ? I've got a weird behavior about that.

If I use the rule "*_Impl", my fr.delcey.pokedexino.data.AppDatabase_Impl is not excluded. If I use the rule "*_Impl*", my fr.delcey.pokedexino.data.AppDatabase_Impl is excluded. If I use the rule "*_Impl.java", my fr.delcey.pokedexino.data.AppDatabase_Impl is not excluded. If I use the rule "*_Impl.kt", my fr.delcey.pokedexino.data.AppDatabase_Impl is not excluded. If I use the rule "*_Impl.*", my fr.delcey.pokedexino.data.AppDatabase_Impl is not excluded.

Note that AppDatabase_Impl is a generated .java file. image

shanshin commented 2 years ago

No, file name extensions are never part of a class name in Java or in Kotlin.

Most likely, you were confused by the fact that the rule *_Impl did not work. Here the situation was complicated by the peculiarity of the JVM and the reporter.

If a class contains a nested class or a lambda expression, then a class with the same name is created in the JVM, but $smth is added at the end. If you open the AppDatabase_Impl class in the report, you will see that the coverage is shown inside only for the AppDatabase_Impl$1 class (it looks like the class contained a lambda expression).

To completely eliminate such situations , you can add two rules

    "*_Impl",
    "*_Impl\$*"
NinoDLC commented 2 years ago

Very interesting! Thanks a lot for your time and explainations :)

Out of thoughts, shouldn't nested classes or lambda expressions be automatically excluded if their englobing classes are excluded ?

shanshin commented 2 years ago

Out of thoughts, shouldn't nested classes or lambda expressions be automatically excluded if their englobing classes are excluded ?

This is an ambiguous issue. Nested classes definitely cannot be excluded, but lambdas are very often difficult to distinguish from custom classes. Therefore, at the moment, the exception via "*name$*" remains the most versatile and flexible way.

NinoDLC commented 2 years ago

For any future Googler (if any), here's my full implementation for excluding Hilt generated files with Kover.

build.gradle (module) :


def excludedFromCoverage = [
        "dagger.hilt.*",
        "hilt_aggregated_deps.*",
        "*_Factory",
        "*_Factory\$*",
        "*_*Factory",
        "*_*Factory\$*",
        "*.Hilt_*",
        "*_HiltModules",
        "*_HiltModules\$*",
        "*_Impl",
        "*_Impl\$*",
        "*.AppModule", // Your Hilt modules should be there if you don't want to test them
        "*.DataModule",
        "*.DomainModule",
]

...

android {
    testOptions {
        unitTests.all {
            if (it.name == "testReleaseUnitTest") {
                kover {
                    disabled = false
                    excludes = excludedFromCoverage
                }
            } else if (it.name == "testDebugUnitTest") {
                kover {
                    disabled = true // won't run unit tests twice (once in debug mode, once in release mode)
                }
            }
        }
    }
}

...

tasks.koverHtmlReport {
    excludes = excludedFromCoverage
}

PS: Current version is 0.5, it is likely that future versions will simplify the excluding of files.

irgaly commented 2 years ago

In my case, excludes = listOf("*") setting in unitTests and koverHtmlReport does not effect. (Kover 0.5.1)

I'll wait next release... 😢

NinoDLC commented 1 year ago

In my case, excludes = listOf("*") setting in unitTests and koverHtmlReport does not effect. (Kover 0.5.1)

I'll wait next release... 😢

What are you trying to achieve ? With * you'd just exclude every single file from coverage, so why use Kover at all ?

irgaly commented 1 year ago

Sorry not enough explanation. I'd like to know whether Kover recognize the exclude options or not by setting excludes = listOf("*"). But the result has all classes, so I think the eclude options are not recognized.


Today, I try again, then the excludes option works correctly 👍

I already discarded commit log of https://github.com/Kotlin/kotlinx-kover/issues/186#issuecomment-1157688405, so I can't discover what my mistake was. sorry...

This is working configuration in my project:

build.gradle.kts

plugins {
...
    id("org.jetbrains.kotlinx.kover") version "0.5.1"
}
...
android {
    ...
    testOptions {
        unitTests.all {
            it.configure<KoverTaskExtension> {
                isDisabled = !it.name.contains("Debug")  // only executes test for debug build type
                excludes = listOf("*") // "*" ignores all classes, so it results empty report.
            }
        }
    }
}

tasks.koverHtmlReport {
    excludes = listOf("*") // "*" ignores all classes, so it results empty report.
}
tugceaktepe commented 1 year ago

Hi, in the current version of the API, the filter for instrumentation and the filter for reports are configuring in different places. In your example, you excluded a class from instrumentation - in this case, coverage for such a class will always be 0 in the report.

To exclude a class from a report, you just need to add an exclusion rule to the report generation task:

tasks.koverHtmlReport {
    excludes = [
        "*_*Factory.*",
        "*_Factory.*",
        "Hilt_*",
        "*_Hilt*",
        ]
}

In the upcoming releases, the API will be redesigned and it will be enough to specify the filter only in one place.

Duplicates #162

Hi @shanshin , As you said that I understood, when we add an exclusion for a class or package, we exclude only from instrumentation, The classes that we excluded, they continues to stay on the coverage report but they have zero coverage rate. Did I understand correctly ?

Thank you.

shanshin commented 1 year ago

@tugceaktepe, hi, the API has been redesigned in the latest version. For which version is your question relevant?

tugceaktepe commented 1 year ago

Hi @shanshin I mean version 0.6.0.