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

Code Coverage not reported for instrumented tests (Android library module) on AGP 7.0 #36

Closed
raquezha opened this issue May 7, 2021 · 24 comments · Fixed by #43 or #53
Closed
Labels
bug Something isn't working
Milestone

Comments

@raquezha
Copy link
Contributor

raquezha commented May 7, 2021

Hi I'm trying this on my test multi-module project before trying this on the other project I'm working on. I've followed the steps and run ./gradlew rootCoveragePlugin but got this report:

Screen Shot 2021-05-07 at 5 17 43 PM

--
com.raquezha.heograpiya.splash is 0% but when I run it in Android Studio local coverage it recognize the tests:

Screen Shot 2021-05-07 at 5 59 14 PM

--

here's the file structure
as you can see Android Studio recognizes the local code coverage of splash module and says its 100% lines covered

Screen Shot 2021-05-07 at 6 08 57 PM

here's my top-level build.gradle

// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
    apply from: rootProject.file('dependencies.gradle')

    repositories {
        google()
        mavenCentral()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:7.0.0-alpha15'
        classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:1.5.0'
        classpath 'com.google.gms:google-services:4.3.5'
        classpath 'nl.neotech.plugin:android-root-coverage-plugin:1.4.0'

        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
}

plugins {
    id "nl.neotech.plugin.rootcoverage" version "1.4.0"
}

apply plugin: 'nl.neotech.plugin.rootcoverage'

jacoco {
    toolVersion = "0.8.7"
}

tasks.withType(Test) {
    useJUnitPlatform()
    testLogging {
        exceptionFormat "full"
        events "started", "skipped", "passed", "failed"
        showStandardStreams true
    }
}

subprojects {
    afterEvaluate { project ->
        if (project.hasProperty('android')) {
            android {
                compileSdk 30
                buildToolsVersion "30.0.3"

                defaultConfig {
                    minSdk 26
                    targetSdk 30
                    versionCode 1
                    versionName "1.0"

                    testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
                }

                buildTypes {
                    release {
                        minifyEnabled false
                        proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
                    }

                    debug {
                        testCoverageEnabled true
                    }
                }

                compileOptions {
                    sourceCompatibility JavaVersion.VERSION_1_8
                    targetCompatibility JavaVersion.VERSION_1_8
                }

                kotlinOptions {
                    jvmTarget = '1.8'
                }

                buildFeatures {
                    viewBinding true
                }

                sourceSets {
                    main {
                        java.srcDirs = ['src/main/kotlin']
                    }
                    androidTest {
                        java.srcDirs = ['src/androidTest/kotlin']
                    }
                    test {
                        java.srcDirs = ['src/test/kotlin']
                    }
                }
            }
        }
    }
}

task clean(type: Delete) {
    delete rootProject.buildDir
}

here's the splash module build.gradle:

plugins {
    id 'com.android.library'
    id 'kotlin-android'
}

dependencies {
    implementation _libs.lottie

    implementation project(':shared')

    testImplementation project(path: ':shared', configuration: 'testDependencies')
    androidTestImplementation project(path: ':shared', configuration: 'androidTestDependencies')
}

tasks.withType(Test) {
    useJUnitPlatform()
    testLogging {
        exceptionFormat "full"
        events "started", "skipped", "passed", "failed"
        showStandardStreams true
    }
}

I'm using:

  1. Android Studio Arctic Fox | 2020.3.1 Canary 15
  2. Gradle JDK: AdoptOpenj9-11
  3. gradle-wrapper: distributionUrl=https://services.gradle.org/distributions/gradle-7.0-bin.zip

I'm using canary because this is only a test project I want to test new stuff

@Rolf-Smit
Copy link
Contributor

Rolf-Smit commented May 26, 2021

I'm not exactly sure what causes this, but I suspect it has something to do with using:

subprojects {
    afterEvaluate { project ->
        // Modifying srcDirs here
    }
}

It could be that the plugin gets configured before you configure the new srcDirs, meaning that the plugin will not see these? You could try to move the plugin apply block to after this block. But this is just me guessing.

Can you share this project with me so I can do some testing?

@raquezha
Copy link
Contributor Author

Hi thanks for helping! you can check the project here: https://github.com/raquezha/heograpiya

@raquezha
Copy link
Contributor Author

I've tried applying the apply block after subProjects{ } block but it did not work still 0%

@Rolf-Smit
Copy link
Contributor

Rolf-Smit commented Jun 4, 2021

Hi @raquezha took me a while to take a look at this, but I found a few things:

Looking at the report that is generated, it is missing all execution data, which is needed to calculate coverage. You can check this for your self by going to the sessions part of the report:
Schermafbeelding 2021-06-04 om 13 36 30

This can have multiple causes, but in this case this happens because the Android Gradle Plugin updated where these files are located since version 7.0. This means the Plugin needs a small update.

However I also spotted this, which will also prevent coverage (even if I update the plugin):

In the root project build.gradle file you have this line:

getExecutionData().setFrom(fileTree(buildDir).include("/jacoco/*.exec"))

This basically overrides any of the default set execution data location's with the location you provided. Your location only looks at the buildDir of the root project, but normally execution data is generated in all the different module buildDir's, so this will definitely not work if you ask me.

TLDR: This plugin needs an update due to changes in the Android Gradle Plugin 7.0. I'm going to take care of this. (see update this is no longer something I can fix)

@Rolf-Smit Rolf-Smit changed the title [Question] Code Coverage 0% Code Coverage 0% when using Android Gradle Plugin 7.0 (alpha/beta) Jun 4, 2021
@Rolf-Smit Rolf-Smit added the bug Something isn't working label Jun 4, 2021
@Rolf-Smit Rolf-Smit modified the milestones: Release 1.4, Release 1.5 Jun 4, 2021
@raquezha
Copy link
Contributor Author

raquezha commented Jun 4, 2021

Thank you! looking forward for the fix, If you need someone to test, I'll gladly help!

@Rolf-Smit
Copy link
Contributor

Update:
I've done some work on making the plugin work with AGP 7.0. The integration test for this plugin now succeeds (which it didn't before because some coverage was missing).

However, on your specific project, there is still coverage missing. So I'm investigating what exactly causes that.

@Rolf-Smit
Copy link
Contributor

Update 2:

Kotlin 1.5 is the issue?

Did some more testing and I found that even though there is a small change in AGP 7.0 (they moved the location of the .exec for unit tests to a different location). After fixing that there is still no unit-test coverage (instrumented unit-tests work fine).

I found that the generated .exec file for unit-tests is basically empty, which is weird.

When I downgrade Kotlin to version 1.4.x everything works as expected, the ` In other words, it seems this is not specifically a AGP 7.0 issue, but more likely a JaCoCo or Kotlin issue. On the other hand AGP 7.0 itself depends on Kotlin 1.4, so maybe that is what's causing the issue?

I also tried upgrading to Gradle 7.1, JaCoCo 8.8 (snapshot), Kotlin 1.5.10. But that didn't work.

For now I only know that downgrading to Kotlin 1.4, seems to work (as a workaround).

Coverage in Android Studio

Now the reason you are seeing coverage in Android Studio is because you are probably running the Intellij code coverage runner, and not the Jacoco coverage runner. If you switch to Jacoco you will notice that Android Studio will not report any coverage.

image

I will try to search further but as of now I don't have any idea's left. Maybe I should file a bug? But where? JaCoCo? Kotlin?

@Rolf-Smit Rolf-Smit changed the title Code Coverage 0% when using Android Gradle Plugin 7.0 (alpha/beta) Code Coverage 0% when using Kotlin 1.5 for local unit tests Jun 28, 2021
@kherink
Copy link

kherink commented Jul 19, 2021

hi, is there any progress on the kotlin 1.5 coverage issue? if help is needed let me know.

@Rolf-Smit
Copy link
Contributor

@kherink I could use some help here in finding out which tool actually causes the missing coverage. I'm currently working on a bare-minimal example that reproduces this (without my Plugin being involved), it's work in progress but at least I'm able to reproduce it*.

*Although it is already reproducible by running code coverage via Android Studio (IntelliJ) using JaCoCo, thus skipping this plugin, the Gradle JaCoCo plugin and Android Gradle plugin.

@kherink
Copy link

kherink commented Jul 22, 2021

@Rolf-Smit I will have a look and let you know what I find.

@CoenQian
Copy link

@kherink I could use some help here in finding out which tool actually causes the missing coverage. I'm currently working on a bare-minimal example that reproduces this (without my Plugin being involved), it's work in progress but at least I'm able to reproduce it*.

*Although it is already reproducible by running code coverage via Android Studio (IntelliJ) using JaCoCo, thus skipping this plugin, the Gradle JaCoCo plugin and Android Gradle plugin.

@Rolf-Smit hi, you can reproduce it by this example.
Wrong coverage after update Kotlin to 1.5.10

@kherink
Copy link

kherink commented Aug 12, 2021

@Rolf-Smit i'll look at it this weekend

@AshMosahebTAB
Copy link

AshMosahebTAB commented Aug 25, 2021

Hi I don't use this plugin.. But I came across this issue when finding the same problem with code coverage with my own custom script after upgrading to AGP 7+ and Kotlin 1.5+.
After extensive messing around with my script and other configurations, I found that this line seemed to be the culprit...

testCoverageEnabled true
in here:

android {
    buildTypes {
        debug {
            testCoverageEnabled true
        }
    }
}

Coverage now seems to now work for me (at least my test reports now show hits), by omitting this line. Not sure what has changed in AGP, and if this effects anything else, but I thought i'd share here anyway, just it case it helps someone, or helps progress the investigation further.

@raquezha
Copy link
Contributor Author

This library needs testCoverageEnabled true in order to work. But thanks anyway, this can be useful for someone who encounter problem same as yours.

@Rolf-Smit
Copy link
Contributor

@AshMosahebTAB

Thanks for taking the time to add this comment here! I also noticed this but figured it was a bug, since you would expect that when this property is true it would actually instrument the classes, omitting it would make me think it would not instrument the classes.

Anyhow, this library indeed uses the testCoverageEnabled property to see which modules to include in the combined coverage report. If removing this property works I can rework the library to no longer use that property, possibly introducing a different method to tell the plugin which modules to use for the combined report.

@trietbui85
Copy link

trietbui85 commented Sep 9, 2021

Hi @Rolf-Smit Does the testCoverageEnabled mean to run coverage for JUnit test (/test - both pure Java/Kotlin & Android unit test), or Instrumented test (/androidTest), or both?

@AshMosahebTAB
Copy link

It's worth noting, that this bug has been raised here https://issuetracker.google.com/issues/195860510

@Rolf-Smit
Copy link
Contributor

@AshMosahebTAB thanks for sharing that here. I'm at this point 100% sure that this is not something I can fix, but this is something that needs fixing on the Android (Google) side of things.

@Rolf-Smit
Copy link
Contributor

Rolf-Smit commented Sep 20, 2021

@anticafe

Hi @Rolf-Smit Does the testCoverageEnabled mean to run coverage for JUnit test (/test - both pure Java/Kotlin & Android unit test), or Instrumented test (/androidTest), or both?

This library does not introduce testCoverageEnabled (it is a property that is part of the Android Gradle Plugin), this library just looks at that property to determinate whether or not to create coverage report tasks for a certain module. This property does not dictate whether or not certain tests run.

In the README you can find information on how to configure this library in such a way that it either executes instrumented tests (test that run on an Android device) or pure Java unit tests (tests that run on your machine): https://github.com/NeoTech-Software/Android-Root-Coverage-Plugin#3-configuration

@Rolf-Smit
Copy link
Contributor

Update:

It seems Google finally stepped in and fixed the issue in AGP version 7.2.0: https://issuetracker.google.com/issues/195860510#comment12

Unfortunately the fix will not be released this month, they expect to release a beta version of AGP 7.2.0 that includes this fix around December 13th.

@Rolf-Smit Rolf-Smit changed the title Code Coverage 0% when using Kotlin 1.5 for local unit tests Code Coverage not reported for instrumented tests (Android library module) on AGP 7.0 Dec 23, 2021
Rolf-Smit added a commit that referenced this issue Dec 23, 2021
In Android Gradle Plugin 7.0 a bug was introduced causing instrumented coverage for Android library projects to fail (https://issuetracker.google.com/issues/195860510). This bug has been fixed in AGP 7.2.0-alpha06. This contains the necessary changes to make this plugin work with AGP 7.2 and some general dependency upgrades:

- Increased the target and compile SDK version to API level 31 for the test fixtures. This also requires the build to use Java 11.
- Upgraded several test related dependencies (AppCompat,  Google Truth, Robolectric etc.).
- Upgraded to Kotlin 1.5

Fixes: #36
@Rolf-Smit
Copy link
Contributor

A SNAPSHOT release is now available that supports Android Gradle Plugin version 7.2.0-alpha06.

Since this SNAPSHOT is only available in Maven Central (Gradle Plugin Portal does not support SNAPSHOT releases), you cannot use this release using the normal id block, instead you will need to add it using an older method:

apply plugin: 'nl.neotech.plugin.rootcoverage'

buildscript {
    repositories {
        // ...
        mavenCentral()
    }
   dependencies {
      classpath 'nl.neotech.plugin:android-root-coverage-plugin:1.5.0-SNAPSHOT'
   }
}

Once there is a stable 7.2. release I will release a stable 1.5 release (or when Google decides to fix this on top of 7.0.4 or 7.1-beta I will create a special release for those).

@jernej7
Copy link

jernej7 commented Mar 1, 2022

@Rolf-Smit
I tried to switch from 1.4.0 to 1.5.0-SNAPSHOT using the same code you provided above, but Gradle reports an error Could not find nl.neotech.plugin:android-root-coverage-plugin:1.5.0-SNAPSHOT., while it has no problems with 1.4.0 using the same method.

Searched in the following locations:
  - https://repo.maven.apache.org/maven2/nl/neotech/plugin/android-root-coverage-plugin/1.5.0-SNAPSHOT/maven-metadata.xml
  - https://repo.maven.apache.org/maven2/nl/neotech/plugin/android-root-coverage-plugin/1.5.0-SNAPSHOT/android-root-coverage-plugin-1.5.0-SNAPSHOT.pom

At these locations only versions up to 1.4.0 are available (at least publicly visible).
I am not familiar with snapshot versions. Do I have to add a specific maven (snapshot) repository for the 1.5.0-SNAPSHOT to work?

@Rolf-Smit
Copy link
Contributor

Rolf-Smit commented Mar 1, 2022

@jernej7

You indeed need to use a special repository, the snapshot repository from Maven Central:

maven {
    url 'https://s01.oss.sonatype.org/content/repositories/snapshots/'
}

You must add this reposiotry to your plugin repository section. Also, since this is not the official Gradle Plugin Portal repository, you might also need to map the plugin ID to an actual maven module (only needed for SNAPSHOTS, or when not using the official Gradle Plugin Portal repository):

settings.gradle (top of the file):

pluginManagement {
    repositories {
        mavenCentral()
        maven {
            url 'https://s01.oss.sonatype.org/content/repositories/snapshots/'
        }
        gradlePluginPortal()
    }
}

@Rolf-Smit
Copy link
Contributor

Edit:

I might have been stupid and never published a special Gradle Plugin Marker artifact to maven central, without a Marker artifact Gradle cannot map the plugin ID to a maven module.

For SNAPSHOTS this is now fixed (I just published a new one), so you no longer need this to use SNAPSHOTS:

    resolutionStrategy {
        eachPlugin {
            if (requested.id.id == 'nl.neotech.plugin.rootcoverage') {
                useModule("nl.neotech.plugin:android-root-coverage-plugin:${requested.version}")
            }
        }
    }

@Rolf-Smit Rolf-Smit mentioned this issue May 12, 2022
Rolf-Smit added a commit that referenced this issue May 12, 2022
Google finally released a stable version of the Android Gradle Plugin 7.2 that fixes #36, this means we can finally create a new stable release for this plugin:

- Most of the plugin has been rewritten to only make use of public Android Gradle Plugin API's, functionally this should not change anything compared to previous releases.
- Adds a minimum required Android Gradle Plugin version check, using an Android Gradle Plugin version that is too old will result in a Gradle build error.
- No longer logs warning messages for (sub)projects (modules) that do not use the Android Gradle Plugin. Instead this is now an info message. This info message will only show for (sub)projects that have a build.gradle file (see #45).
- No longer logs warning messages asking to apply the JaCoCo plugin yourself, instead the plugin takes care of this now.
- Potentially fixes #42, since JaCoCo will now always be applied by the plugin at the root project.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
7 participants