I am trying to figure out a way to coverage for these source Groovy files when they are compiled and executed in a Jenkins runtime. I've documented my journey so far in https://github.com/mkobit/jenkins-pipeline-shared-libraries-gradle-plugin/issues/32 but here is a summary of where I am currently at:
* I can get execution data for the classes
* Dumping classpath shows multiple instances of the library classes (1 per test they are used in)
* Attempting to get a report fails with `java.lang.IllegalStateException: Can't add different class with same name: com/mkobit/libraryexample/ExampleSrc`. There are a few other duplicates as well
* Filtering down the report to only a single specific file allows me to generate a coverage report, but I only get it for the test was filtered (https://github.com/mkobit/jenkins-pipeline-shared-libraries-gradle-plugin/issues/32#issuecomment-366371128)
What I want to try and figure out is, what is the right way forward to "merge" the execution data together for the original source file? It sounds similar-ish to https://github.com/jacoco/jacoco/issues/197 so I'll add a comment there as well.
Here are some reproduction steps:
1. `git clone -b 'mk/coverage-investigation' https://github.com/mkobit/jenkins-pipeline-shared-library-example.git`
2. `cd jenkins-pipeline-shared-library-example`
3. `./gradlew integrationTest jacocoIntegrationTestReport -s ==> See error mentioned above about duplicate classnames
4. `find build/jacoco/classpathdumps/com/mkobit/libraryexample/ExampleSrc.* | head -n 1 | xargs -n 1 basename` ==> this will get one of the classes. for me this time, it was "ExampleSrc.99203068b63d74ec.class"
5. Replace the class name at https://github.com/mkobit/jenkins-pipeline-shared-library-example/blob/6d268d56c86c626312bc76a9e00051c5749c28d1/build.gradle.kts#L52 with the class name above and uncomment the line
6. `./gradlew jacocoIntegrationTestReport` ==> Generate report for single class
7. Open HTML report at build/reports/jacoco/jacocoIntegrationTestReport/html/index.html and see results for class
8. find build/jacoco/classpathdumps/com/mkobit/libraryexample/ExampleSrc.* | tail -n 1 | xargs -n 1 basename ==> get name for other class
9. Repeat steps 4-7 with result from other class to see different
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package com.mkobit.libraryexample;
import com.cloudbees.groovy.cps.Builder;
import com.cloudbees.groovy.cps.MethodLocation;
import com.cloudbees.groovy.cps.NonCPS;
import com.cloudbees.groovy.cps.WorkflowTransformed;
import com.cloudbees.groovy.cps.impl.CpsCallableInvocation;
import com.cloudbees.groovy.cps.impl.CpsFunction;
import com.cloudbees.groovy.cps.sandbox.Trusted;
import com.mkobit.libraryexample.ExampleSrc._nonCpsDouble_closure1;
import groovy.lang.GroovyObject;
import groovy.lang.MetaClass;
import java.io.Serializable;
import java.util.List;
import java.util.Objects;
import org.codehaus.groovy.runtime.ArrayUtil;
import org.codehaus.groovy.runtime.ScriptBytecodeAdapter;
import org.codehaus.groovy.runtime.callsite.CallSite;
import org.jenkinsci.plugins.workflow.cps.CpsClosure2;
import org.jenkinsci.plugins.workflow.cps.Safepoint;
@WorkflowTransformed
public class ExampleSrc implements Serializable, GroovyObject {
private final Object script;
private static final CpsFunction ___cps___0;
private static long __timeStamp;
public ExampleSrc(Object script) {
CallSite[] var2 = $getCallSiteArray();
MetaClass var3 = this.$getStaticMetaClass();
this.metaClass = var3;
Object var4 = var2[0].call(Objects.class, script);
this.script = var4;
}
@WorkflowTransformed
public void sayHelloTo(String name) {
CallSite[] var2 = $getCallSiteArray();
throw (Throwable)var2[1].callConstructor(CpsCallableInvocation.class, ___cps___0, this, new Object[]{name});
}
@NonCPS
public List<Integer> nonCpsDouble(List<Integer> integers) {
CallSite[] var2 = $getCallSiteArray();
return (List)ScriptBytecodeAdapter.castToType(var2[2].call(integers, new _nonCpsDouble_closure1(this, this)), List.class);
}
@WorkflowTransformed
private static final CpsFunction ___cps___0() {
CallSite[] var0 = $getCallSiteArray();
Builder b = (Builder)ScriptBytecodeAdapter.castToType(var0[3].call(var0[4].call(var0[5].callConstructor(Builder.class, var0[6].callConstructor(MethodLocation.class, "com.mkobit.libraryexample.ExampleSrc", "sayHelloTo", "/tmp/jenkinsTests.tmp/jenkins1133211585081614104test/jobs/project/builds/1/libs/testLibrary/src/com/mkobit/libraryexample/ExampleSrc.groovy")), CpsClosure2.class), var0[7].callGetProperty(Trusted.class)), Builder.class);
return (CpsFunction)ScriptBytecodeAdapter.castToType(var0[8].callConstructor(CpsFunction.class, ScriptBytecodeAdapter.createList(new Object[]{"name"}), var0[9].call(b, var0[10].call(b, 14, Safepoint.class, "safepoint"), var0[11].call(b, var0[12].call(b, ArrayUtil.createArray(14, var0[13].call(b, 14, var0[14].call(b), "script"), var0[15].call(b, "echo"), false, var0[16].call(b, 14, var0[17].call(b, var0[18].call(b, 14, "name")), var0[19].call(b, var0[20].call(b, "Hello there "), var0[21].call(b, "")))))))), CpsFunction.class);
}
static {
Long var0 = 0L;
__timeStamp = var0;
Object var1 = $getCallSiteArray()[22].callStatic(ExampleSrc.class);
___cps___0 = (CpsFunction)ScriptBytecodeAdapter.castToType(var1, CpsFunction.class);
}
}
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package com.mkobit.libraryexample;
import com.cloudbees.groovy.cps.Builder;
import com.cloudbees.groovy.cps.MethodLocation;
import com.cloudbees.groovy.cps.NonCPS;
import com.cloudbees.groovy.cps.WorkflowTransformed;
import com.cloudbees.groovy.cps.impl.CpsCallableInvocation;
import com.cloudbees.groovy.cps.impl.CpsFunction;
import com.cloudbees.groovy.cps.sandbox.Trusted;
import com.mkobit.libraryexample.ExampleSrc._nonCpsDouble_closure1;
import groovy.lang.GroovyObject;
import groovy.lang.MetaClass;
import java.io.Serializable;
import java.util.List;
import java.util.Objects;
import org.codehaus.groovy.runtime.ArrayUtil;
import org.codehaus.groovy.runtime.ScriptBytecodeAdapter;
import org.codehaus.groovy.runtime.callsite.CallSite;
import org.jenkinsci.plugins.workflow.cps.CpsClosure2;
import org.jenkinsci.plugins.workflow.cps.Safepoint;
@WorkflowTransformed
public class ExampleSrc implements Serializable, GroovyObject {
private final Object script;
private static final CpsFunction ___cps___3;
private static long __timeStamp;
public ExampleSrc(Object script) {
CallSite[] var2 = $getCallSiteArray();
MetaClass var3 = this.$getStaticMetaClass();
this.metaClass = var3;
Object var4 = var2[0].call(Objects.class, script);
this.script = var4;
}
@WorkflowTransformed
public void sayHelloTo(String name) {
CallSite[] var2 = $getCallSiteArray();
throw (Throwable)var2[1].callConstructor(CpsCallableInvocation.class, ___cps___3, this, new Object[]{name});
}
@NonCPS
public List<Integer> nonCpsDouble(List<Integer> integers) {
CallSite[] var2 = $getCallSiteArray();
return (List)ScriptBytecodeAdapter.castToType(var2[2].call(integers, new _nonCpsDouble_closure1(this, this)), List.class);
}
@WorkflowTransformed
private static final CpsFunction ___cps___3() {
CallSite[] var0 = $getCallSiteArray();
Builder b = (Builder)ScriptBytecodeAdapter.castToType(var0[3].call(var0[4].call(var0[5].callConstructor(Builder.class, var0[6].callConstructor(MethodLocation.class, "com.mkobit.libraryexample.ExampleSrc", "sayHelloTo", "/tmp/jenkinsTests.tmp/jenkins8402738745313950554test/jobs/project/builds/1/libs/testLibrary/src/com/mkobit/libraryexample/ExampleSrc.groovy")), CpsClosure2.class), var0[7].callGetProperty(Trusted.class)), Builder.class);
return (CpsFunction)ScriptBytecodeAdapter.castToType(var0[8].callConstructor(CpsFunction.class, ScriptBytecodeAdapter.createList(new Object[]{"name"}), var0[9].call(b, var0[10].call(b, 14, Safepoint.class, "safepoint"), var0[11].call(b, var0[12].call(b, ArrayUtil.createArray(14, var0[13].call(b, 14, var0[14].call(b), "script"), var0[15].call(b, "echo"), false, var0[16].call(b, 14, var0[17].call(b, var0[18].call(b, 14, "name")), var0[19].call(b, var0[20].call(b, "Hello there "), var0[21].call(b, "")))))))), CpsFunction.class);
}
static {
Long var0 = 0L;
__timeStamp = var0;
Object var1 = $getCallSiteArray()[22].callStatic(ExampleSrc.class);
___cps___3 = (CpsFunction)ScriptBytecodeAdapter.castToType(var1, CpsFunction.class);
}
}
--
You received this message because you are subscribed to a topic in the Google Groups "JaCoCo and EclEmma Users" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/jacoco/-zTg9Bt5964/unsubscribe.
To unsubscribe from this group and all its topics, send an email to jacoco+un...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/jacoco/ebabe3aff26f96248b1994370f627290%40mountainminds.com.
For more options, visit https://groups.google.com/d/optout.
Hi Michael,
thanks for the details! Indeed a variable name and a value in the constant pool are different. This leads to a different class id which is simply the CRC64 checksum of the raw class file. So current JaCoCo will not be able to merge them.
The tricky question is how and under what circumstances IClassCoverage instances can be merged. A conservative approach (which would work in your specific case) would be to allow merges only if both classes have the exact same instruction/branch structure.
Regards,
-marc
You received this message because you are subscribed to the Google Groups "JaCoCo and EclEmma Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to jacoco+un...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/jacoco/CALELY9HB0ceYR%3DWve2jYuG2zMV9KomJr1YFnWtP7h8gEubAyLg%40mail.gmail.com.