evant / gradle-retrolambda

A gradle plugin for getting java lambda support in java 6, 7 and android
Apache License 2.0
5.3k stars 449 forks source link

Support for CodeCoverage? #145

Open BenjaminZaiser opened 8 years ago

BenjaminZaiser commented 8 years ago

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

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

evant commented 8 years ago

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.

jaredsburrows commented 8 years ago

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

jaredsburrows commented 8 years ago

@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 commented 8 years ago

@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 commented 8 years ago

@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!

BenjaminZaiser commented 8 years ago

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?

jaredsburrows commented 8 years ago

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

jaredsburrows commented 8 years ago

@evant I think this can be closed now.

BenjaminZaiser commented 8 years ago

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.

jaredsburrows commented 8 years ago

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

ultraon commented 7 years ago

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

ghost commented 7 years ago

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.

vudzkostek commented 7 years ago

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

NateWickstrom commented 7 years ago

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)

mlc commented 7 years ago

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.

poldz123 commented 7 years ago

Any update on this?