gradle-retrolambda icon indicating copy to clipboard operation
gradle-retrolambda copied to clipboard

Support for CodeCoverage?

Open BenjaminZaiser opened this issue 10 years ago • 16 comments

We are using Retrolambda in our app (which is really awesome and makes our code much cleaner) along with the jacoco plugin to gather the code coverage of our acceptance tests (see also http://stackoverflow.com/questions/30976319/android-gradle-jacoco-offline-instrumentation-for-integration-tests).

So we are

  • building and instrumenting the app (retrolambda converts the lambdas into anonymous classes)
  • deploying the app to the smartphone
  • executing the test cases on the smartphone (by using appium)

Then Jacoco writes the coverage information to an exec file. This file contains a list of classes which were executed during the tests, e.g. com/ecma/app/service/Manager or com/ecma/app/model/Model$$Lambda$15

When SonarQube then tries to calculate the code coverage by comparing the coverage information with the source code, it will fail, because there is no anonymous class Model$$Lambda$15 in the source code.

Is there a way to fix this problem?

Thanks Ben

BenjaminZaiser avatar Oct 30 '15 08:10 BenjaminZaiser

Not sure. It's the class files being transformed so you don't have any correct sources to compare against. You could write a tool that modifies the test coverage file to convert the classes back to whatever the java 8 lambda representation would be. Not sure how much work that would be.

evant avatar Oct 31 '15 20:10 evant

+1 I really wish I knew this plugin broke Jacoco before I started to use it :(.

jaredsburrows avatar Nov 18 '15 18:11 jaredsburrows

@evant How difficult would that be?

@BenjaminZaiser Check out this project: https://github.com/maurizi/Geoclock

It looks like the following combinations work:

The Repo:

        classpath 'com.android.tools.build:gradle:1.3.0'
        classpath 'me.tatarka:gradle-retrolambda:3.2.0'

and mine:

        classpath 'com.android.tools.build:gradle:1.5.0'
        classpath 'me.tatarka:gradle-retrolambda:3.2.0'

Once you bump the retrolambda plugin to 3.2.1, it no longer works.

I modified the jacoco task to filter the Lambdas that are auto-generated

task jacocoTestReport(type: JacocoReport, dependsOn: "testDebugUnitTest") {
    group = "Reporting"
    description = "Generate Jacoco coverage reports after running tests."
    reports {
        xml.enabled = true
        html.enabled = true
    }
    classDirectories = fileTree(
            dir: './build/intermediates/classes/debug',
            excludes: ['**/R.class',
                       '**/R$*.class',
                       '**/*$InjectAdapter.class',
                       '**/*$ModuleAdapter.class',
                       '**/*$ViewInjector*.class',
                       '**/Lambda$*.class',
                       '**/Lambda.class',
                       '**/*Lambda.class',
                       '**/*Lambda*.class'
            ])

    additionalSourceDirs = files(coverageSourceDirs)
    sourceDirectories = files(coverageSourceDirs)
    executionData = files('build/jacoco/testDebugUnitTest.exec')
}

jaredsburrows avatar Dec 17 '15 17:12 jaredsburrows

@evant I have tried the following versions:

Works:

classpath 'me.tatarka:gradle-retrolambda:3.2.0'

Fails to produce jacoco:

classpath 'me.tatarka:gradle-retrolambda:3.2.1'
classpath 'me.tatarka:gradle-retrolambda:3.2.4'
classpath 'me.tatarka:gradle-retrolambda:3.3.0-beta3'

jaredsburrows avatar Dec 17 '15 17:12 jaredsburrows

@evant @BenjaminZaiser Using:

        classpath 'com.android.tools.build:gradle:1.5.0'
        classpath 'me.tatarka:gradle-retrolambda:3.2.4' // latest that is not in beta
        classpath 'me.tatarka.retrolambda.projectlombok:lombok.ast:0.2.3.a2'

and:

task jacocoTestReport(type: JacocoReport, dependsOn: "testDebugUnitTest") {
    group = "Reporting"
    description = "Generate Jacoco coverage reports after running tests."
    reports {
        xml.enabled = true
        html.enabled = true
    }
        def autoGenerated = ['**/R.class',
                             '**/R$*.class',
                             '**/Manifest*.*',
                             'android/**/*.*',
                             '**/BuildConfig.*',
                             '**/*$ViewBinder*.*',
                             '**/*$ViewInjector*.*',
                             '**/Lambda$*.class',
                             '**/Lambda.class',
                             '**/*Lambda.class',
                             '**/*Lambda*.class']

    classDirectories = fileTree(
            dir: './build/intermediates/classes/debug',
            excludes: autoGenerated)
    // additionalSourceDirs = files(coverageSourceDirs) // should not matter
    sourceDirectories = files(coverageSourceDirs)
    executionData = files('build/jacoco/testDebugUnitTest.exec')
}

I got it to work!

jaredsburrows avatar Jan 08 '16 22:01 jaredsburrows

Thanks for your comment, but I don't know how you got it to work so the anonymous classes (mentioned in the exec file) are mapped to the lambdas in the code?

BenjaminZaiser avatar Mar 24 '16 16:03 BenjaminZaiser

@BenjaminZaiser Yes. I exclude them out. What do you need help with?

jaredsburrows avatar Mar 24 '16 23:03 jaredsburrows

@evant I think this can be closed now.

jaredsburrows avatar Mar 24 '16 23:03 jaredsburrows

But then the overall code coverage is wrong (or way below the actual code coverage), because the code inside the lambdas is not analyzed; you don't know if the code inside the lambdas was executed or not.

BenjaminZaiser avatar Mar 29 '16 06:03 BenjaminZaiser

@BenjaminZaiser It is the not the best but it is very close.

jaredsburrows avatar Mar 29 '16 08:03 jaredsburrows

Hello, i've improved gradle script for Jacoco, please see here: https://gist.github.com/ultraon/54cca81ca159ed0a4a9ebf62e89c26ba

ultraon avatar Nov 17 '16 13:11 ultraon

This seems to break again when I use a newer version than 3.2.4 (tried 3.3.1 and 3.4.0). Currently I use 3.2.4 in combination with retrolambda 2.3.0 and got it to work (with the configuration from @ultraon 👍 ).

When I use a newer version I get either an IllegalAccessError for $jacocoData or a NoSuchMethodError if I don't use defaultMethods also for $jacocoData (or $jacocoInit).

The errors are raised for the methods on an Interface.

ghost avatar Nov 24 '16 10:11 ghost

I confirm. When using version 3.2.4 I get proper jacoco reports. when I change to 3.2.5 or any higher (up to 3.5.0) jacoco code coverage shows reduction from 73% to 34%. Going through particular tests it seems like with retrolambda many tests are not triggered and therefore there are no results, ergo 0% of coverage.

[Edit] Also, i checked changes between 3.2.4 and 3.2.5 and it seems that changes in groovy files are the reason, not versions bumps, but I am not able to conclude which line causes the problem

vudzkostek avatar Jan 26 '17 15:01 vudzkostek

Would appreciate this mentioned in the Known Issues section of the README, since generating coverage reports is a fairly common task and fixing this issue doesn't seem to be a priority... (personally it would have saved me some debug time - I image others too)

NateWickstrom avatar Mar 30 '17 04:03 NateWickstrom

We're seeing this again/still on grandle-retrolambda 3.6.0 with retrolambda 2.5.1.

I did some further digging and found that retrolambda can, under some circumstances, move the jacoco-added method $jacocoInit() from our class Foo into a newly generated class Foo$ which the program when running of course can not find.

Our workaround for now is to make separate builds for instrumentation and for actually running, but this is slightly silly.

mlc avatar Apr 25 '17 19:04 mlc

Any update on this?

poldz123 avatar Jun 15 '17 15:06 poldz123