Kotlin / kotlinx-kover

Apache License 2.0
1.36k stars 53 forks source link

Advanced Android support #18

Closed shanshin closed 1 year ago

shanshin commented 3 years ago

It is necessary to improve the plugin API for convenient work with Android applications:

pvegh commented 2 years ago

Cannot use this until then. It's not realistic for us currently. <===========--> 86% EXECUTING [25m 16s]

shanshin commented 2 years ago

@pvegh, please, can you clarify how long it takes to build a project without applying a Kover, how many tests and classes are in the project. Perhaps you have suggestions on what settings can be added to the plugin to reduce code instrumentation and speed up your build.

pvegh commented 2 years ago

Those details are not important, the problem is that ./gradlew koverHtmlReport is not variant specific. Currently this one Gradle task builds the project in every possible variant, which means debug + release build types for all of paid, free, foo flavours. Meaning it builds 6 times instead of just one (whatever is needed e.g. freeDebug for local or freeRelease for CI).

As soon as it runs for just the specified variant (e.g. ./gradlew koverHtmlReportFreeDebug), it will be a huge win instantly.

SpertsyanKM commented 2 years ago

@pvegh I've solved this issue by simply adding this configuration to android.testOptions.unitTests.all section in build.gradle.

kover {
    disabled = (name == "testReleaseUnitTest")
}

Maybe it will help you?

pvegh commented 2 years ago

Thanks, it might help others. I'm not interested in a workaround in which I must explicitly disable things one by one. If I execute ./gradlew koverHtmlReportLocalDebug then exactly that should be executed, not every variant combination

pvegh commented 2 years ago

I see now that we removed kover from the project, because the wasted time was huge without variants correctly supported.

pagsantos commented 2 years ago

we have the same problem can we at least have the possibility to avoid certain flavors ?

gerardoepitacio commented 2 years ago

@shanshin Is there a way to see the contribution guidelines for this project?, I really wanted to learn how to write a gradle plugin to be able to improve this tool, I would really appreciate a guideline over that.

NeoMindStd commented 2 years ago

We need report tasks support to specific buildVariant. Do you have any plans or schedules to support?

shanshin commented 2 years ago

@NeoMindStd, yes, right now we are developing a new version of the plugin API, after switching to which filtering by buildVariant and/or flavours will be added.

shanshin commented 2 years ago

@gerardoepitacio, at the moment there are no strict documents, you can create PR with your suggestions and bug fixes. Because the project code is not stabilized at the moment, I would not recommend creating large PR. However, if you have ideas, you can create an issue with their description.

samuelneff commented 1 year ago

Could we get an update on the status of this issue? Is a fix under active development?

If not, do you have any pointers on how to fix it? Would development/PR be welcome? I looked into it quickly but I think I'd need a little guidance on where to start addressing it. I'd be happy to help though; this is a huge issue for us as we have two build types and six flavors.

samuelneff commented 1 year ago

Building off of @SpertsyanKM 's answer above, here is a more generic implementation that figures out the right flavor/build-type from the start tasks and works with projects that don't have Android and those with Android but without Kover.

It also will support a mix of projects with different flavors defined or no flavors defined.

// NOTE: This code depends on having a map defined as `buildFlavorConfigs` with the flavors as keys. If you don't have this then you can run this code after everything else is evaluated and gather the flavor names from by looping through the projects first.

// We put these variable declarations inside the buildscript's `ext` block but they can be put locally right below the assignment code instead

startTaskNames = gradle.getStartParameter().getTaskRequests().collect { TaskExecutionRequest req -> req.args }.flatten()

flavorNames = buildFlavorConfigs.keySet().collect { String flavor ->
    flavor.substring(0, 1).toUpperCase() + flavor.substring(1)
}
buildTypes = ['Debug', 'Release']

List<String> selectedFlavors = flavorNames.findAll { flavorName ->
    startTaskNames.any { startTaskName ->
        startTaskName.contains(flavorName)
    }
}
List<String> selectedBuildTypes = buildTypes.findAll { buildType ->
    startTaskNames.any { startTaskName ->
        startTaskName.contains(buildType)
    }
}
selectedBuildType = selectedBuildTypes.size() == 1
        ? selectedBuildTypes[0]
        : buildTypes[0]

switch (selectedFlavors.size()) {
    case 0:
        selectedFlavor = selectedBuildType == 'Debug'
                ? 'Dev'
                : 'Prod'
        break

    case 1:
        selectedFlavor = selectedFlavors[0]
        break

    default:
        selectedFlavor = ''
        break
}

// This code we have in our top-level `build.config` file.

if (startTaskNames.any { it.startsWith('kover')}) {

    String allowedUnitTestNameWithFlavorAndBuild = "test${selectedFlavor}${selectedBuildType}UnitTest".toString()
    String allowedUnitTestNameWithBuildOnly = "test${selectedBuildType}UnitTest".toString()

    subprojects {
        afterEvaluate { project ->
            if (project.hasProperty("android")) {
                android.testOptions.unitTests.all {
                    if (project.hasProperty("kover")) {
                        if (name != allowedUnitTestNameWithFlavorAndBuild && name != allowedUnitTestNameWithBuildOnly) {
                            kover {
                                disabled = true
                            }
                        }
                    }
                }
            }
        }
    }
}
shanshin commented 1 year ago

@samuelneff, work is in progress now. since this is not just a bug, but new features that were not originally laid in the plugin API, it took a redesign of the API for more convenient use in Android.

shanshin commented 1 year ago

@samuelneff, however, it would help us a lot to know how you build reports when there are several build types and flavors in the project.

Do you generate only one report by build type and all flavors, multiple reports at a time for different build types, or only individual reports for each build variant (build type + flavor)?

G00fY2 commented 1 year ago

@samuelneff We have an Android project with a lot of variants (multiple flavors in different dimensions + multiple build types). We are currently using Jacoco without any issues. But even after disabling all test tasks expect for one variant we got attempted duplicate class definition exceptions when using Kover. So I guess there are more underlying issues which have to be fixed instead of only limiting the task execution.

samuelneff commented 1 year ago

work is in progress now.

🍾 🍾 🍾 😄 😄 😄 So happy to hear this. Thank you!!

since this is not just a bug, but new features

Different opinion on my side, but not important. So glad it's being addressed either way.

it would help us a lot to know how you build reports when there are several build types and flavors in the project

I'm happy to explain what we do and it's very possible that what we do is very unusual compared to how others use flavors and build-types. Any feedback on this that people care to share is appreciated (fine to send me a direct message to not clog up this thread with unrelated things, sam@serndesign.com. )

We have five flavors: Dev, Prod, Local, Mock, E2e. They are used for different use cases and are mutually exclusive. There is no reason we would ever run a build for more than one flavor at a time.

Our Flavors primarily are used to define which downstream server to hit. Production, shared development server, local in-development back end, or client-side mocks (no network calls at all). The e2e flavor also adds some extra utility code to support e2e testing.

So for an official build on our CI server we typically run a command like this:

./gradlew clean detekt testProdRelease koverXmlReport assembleProdRelease

and while working on the code we would usually run

./gradlew testDebugUnitTest koverXmlReport

I hope this is useful and am happy to help in any way I can--more info, early testing, whatever.

samuelneff commented 1 year ago

@G00fY2

We are currently using Jacoco without any issues

We actually have been running with Kover for about a year without realizing we had this issue. We always just wondered why our builds were so slow without making it a priority to look into it. It's pretty common in my experience for builds to be slower on CI than locally.

We only discovered the problem when we made a change to our build to surface errors as a giant annotation instead of being buried in the CI console output. Once we did that we immediately saw that all errors were showing up 12 times each.

G00fY2 commented 1 year ago

@samuelneff If you don't have variant specific unit tests and only need to run to run them once, why not disable all tests except for one variant. This way you don't need to wait for changes to kover since there will only be one test task that can be executed:

android {
    androidComponents {
        beforeVariants { variantBuilder ->
            variantBuilder.enableUnitTest = variantBuilder.name == "MyVariantIwantTestsFor"
        }
    }
 }
samuelneff commented 1 year ago

@G00fY2 Thanks for the recommendation. I appreciate it, but am not sure it applies to our (unusual?) use case.

While we only build one variant at a time, we would want to run the unit tests regardless of what variant was run. If we always ran the same variant, like testDevDebugUnitTest then that would require compiling that variant regardless of which one we actually wanted to build. So we would often end up compiling twice, which is not necessary in our specific case and what I'm looking to avoid.

hansenji commented 1 year ago

My team is in a similar space as @samuelneff. We have a few build types that are mutually exclusive and running kover on all build types takes valuable of cpu time.

sonrohan commented 1 year ago

+1 Support for this

adrianlazaro8 commented 1 year ago

+1 for this advanced Android support. Thanks for your work!

outadoc commented 1 year ago

@G00fY2 Thank you for this snippet, it solves our exact needs, for both Kover and just configuring unit tests correctly overall! I'd been trying to figure this out for quite some time 🎉

jweick commented 1 year ago

Hi,

i followed the discussion what can be done to avoid running all the buildType/variants when executing koverReport and tried a variant of a mentioned configuration option

kover { disabled = (name == "testReleaseUnitTest") enabled = name == "testDebugUnitTest" }

to only run it on the testDebugUnitTest task.

That worked only partly, as something like testReleaseUnitTest was indeed not executed (log said it was SKIPPED), but related tasks like bundleReleaseClassesToCompileJar or compileReleaseUnitTestKotlin where executed. And that should not happen, as the job just builds a debug-build.

Any ideas for a workaround until kover supports buildTypes/variants?

regards, Jerg

UPDATE: ok, i see i should be possible with G00fY2's last advice

android { androidComponents { beforeVariants { variantBuilder -> variantBuilder.enableUnitTest = variantBuilder.name == "MyVariantIwantTestsFor" } } }

but that seems not to be availably in a groovy build-script - or am i wrong?

shanshin commented 1 year ago

Resolved in 0.7.0-Alpha

hamada147 commented 8 months ago

I am using the KMP project that supports JVM and AndroidTarget with only two variants debug and release, and I'm still getting the same error above. Using Kover 0.7.5 with JaCoCo engine. If not using JaCoCo, it works with no issues. If you need to reproduce the same issue, our repo is open sourced at Wallet SDK

jtietema commented 5 months ago

I am having the same issue as @hamada147, also getting the duplicate class errors using the Jacoco engine. I am running the freshly released Kover 0.8.0.