bmuschko / gradle-clover-plugin

Gradle plugin for generating a code coverage report using Clover
Apache License 2.0
74 stars 49 forks source link

Unable to get merged report in multi project using spock #115

Open bdbogjoe opened 6 years ago

bdbogjoe commented 6 years ago

I'm not able to get correct coverage report using multi project with spock

After generating report and doing aggregation from root i cannot see coverage for AbstractFoo. Is there something wrong in my project ?

here my sample project : https://github.com/bdbogjoe/gradle-clover-multiproject

After executing :

I can see only coverage data from impl

image

Alex-Vol-SV commented 6 years ago

Thank you for making a demonstration of this.

This is a known issue when doing such type of testing with a module injecting cross-project tests. If you can put your additional tests in the same project they work. There is no easy way to fix this because of the methodology followed when instrumenting code. We instrument code by attaching actions to the test task that recompile all source sets (main and test) for a project. This action is project specific and you have main source code from another project that is not visible in the "core" project. The non-instrumented code is visible via the compile dependency but the instrumented code is not because it only exists while the execution of the test task in the project it is compiled within.

It would take a significant overhaul of the internals of this plugin to allow such coverage to happen. And it would not be an automatic support, you would have to make changes to your build.gradle to introduce it.

One thought that just occurred to me and I am adding it here so I do not forget is to automatically create new source sets for instrumented non-test code which would be allowed to persist beyond the test instrumentation. Perhaps even tied to an instrumentMainCode task that we can declare output dependencies on. Then that source set could be added as a reference instead of the non-instrumented source set in the sibling project with the test tasks.

bdbogjoe commented 6 years ago

thanks for your answer

i tried to add :

clover{
    additionalSourceSet {
        srcDirs = project(':core').sourceSets.main.java.srcDirs
        classesDir = project(':core').sourceSets.main.java.outputDir
    }
}

in impl i have now the source code, but marked as not covered

image

Alex-Vol-SV commented 6 years ago

I would not use the classesDir as the :core project outputDir. Try making it a completely new directory.

classesDir = file("$buildDir/core-instr")

This may be the missing link that gets you what you need. Not pretty but if it works it is a good workaround.

FYI, the above tells the plugin where to place the instrumented code, it is better not to pollute the other project's outputDirectory from the action added on the impl project test task.

I should add more details in the documentation about this usage of additionalSourceSet

bdbogjoe commented 6 years ago

i found a workaround, but do you think it is acceptable : https://github.com/bdbogjoe/gradle-clover-multiproject/tree/workaround

image

Alex-Vol-SV commented 6 years ago

It seems that the workaround as coded would treat the application code as test code. Do you see the application classes in the "Application code" tab? Or do they appear in both that and "Test code".

I hoped the additionalSourceSets would work with my suggestion. That is an extreme case of the purpose of this configuration IMHO.

bdbogjoe commented 6 years ago

i added your suggestion in the branch but without


sourceSets {
    // Note that just declaring this sourceset creates two configurations.
    test {
        java {
            srcDirs += project(':core').sourceSets.main.java.srcDirs
        }
    }
}

i cannot see the code as covered

bdbogjoe commented 6 years ago

my solution looks like now :

clover{
    additionalSourceSet {
        srcDirs = project(':core').sourceSets.main.java.srcDirs
        def coreInstr = file("${buildDir}/core-instr")
        coreInstr.mkdirs()
        classesDir = coreInstr
    }
}

sourceSets {
    // Note that just declaring this sourceset creates two configurations.
    test {
        java {
            srcDirs += project(':core').sourceSets.main.java.srcDirs
        }
    }
}

not nice solution but at least working as expected

Alex-Vol-SV commented 6 years ago

It is surprising to me that you need both additionalSourceSet and the srcDirs addition. I would have thought the srcDirs addition would be enough. Perhaps the intersection helps the process.

I am going to investigate the additionalSourceSet operation across multi-project, never tried it before.