Skip to content

Instantly share code, notes, and snippets.

@bademux
Last active April 16, 2024 19:03
Show Gist options
  • Select an option

  • Save bademux/5210f14fc89b43a99da6fcfe4ad7a62f to your computer and use it in GitHub Desktop.

Select an option

Save bademux/5210f14fc89b43a99da6fcfe4ad7a62f to your computer and use it in GitHub Desktop.
Pure groovy jacoco2cobertura converter to be used with Gradle
task jacoco2cobertura(){
group = 'verification'
description = 'Convert jacoco test report to Cobertura report'
outputs.file("$buildDir/reports/cobertura/test/coberturaTestReport.xml")
doLast {
outputs.files.singleFile.withWriter {
Jacoco2coberturaConverter.jacoco2cobertura(it,
jacocoTestReport.reports.xml.outputLocation.get().asFile,
jacocoTestReport.sourceDirectories.files)
}
}
tasks.jacocoTestReport.finalizedBy it
}
import groovy.xml.XmlNodePrinter
import groovy.xml.XmlSlurper
import groovy.xml.slurpersupport.GPathResult
import groovy.xml.slurpersupport.NodeChild
import groovy.xml.slurpersupport.NodeChildren
import java.util.concurrent.TimeUnit
//put file into buildSrc/src/main/groovy
class Jacoco2coberturaConverter {
private static List<NodeChild> methodLines(NodeChild jMethod, NodeChildren jMethods, List<NodeChild> jLines) {
int startLine = jMethod.@line.toInteger()
int endLine = jMethods.@line.collect({ it.toInteger() }).findAll({ it > startLine }).min() ?: Integer.MAX_VALUE
return jLines.findAll {
var nr = it.@nr.toInteger()
startLine <= nr && nr < endLine
}
}
private static void convertLines(Node parent, List<NodeChild> jLines) {
var lines = parent.appendNode('lines')
for (line in jLines) {
int mb = line.@mb.toInteger()
int cb = line.@cb.toInteger()
int ci = line.@ci.toInteger()
boolean isBranch = mb + cb > 0
var l = lines.appendNode('line', [branch: isBranch.toString(), number: line.@nr.toInteger(), hits: ci > 0 ? '1' : '0'])
if (isBranch) {
int percentage = (int) (100.0 * cb / (cb + mb))
l.'@condition-coverage' = "${percentage}% (${cb}/${cb + mb})"
l.appendNode('conditions').appendNode('condition', [number: '0', type: 'jump', coverage: "${percentage}%"])
}
}
}
private static Map<String, String> calcCounters(GPathResult source) {
var fraction = (covered, missed) -> covered == 0 ? 0.0 : covered / (covered + missed)
['line-rate' : count(source, 'LINE', fraction).toString(),
'branch-rate': count(source, 'BRANCH', fraction).toString(),
complexity : count(source, 'COMPLEXITY', Double::sum).toString()]
}
private static double count(GPathResult source, String type, Closure operation) {
var c = source.counter.find { it.@type == type }
c ? operation(c.@covered.toDouble(), c.@missed.toDouble()) : 0
}
private static Node convertClass(Node parent, NodeChild jCls, GPathResult sourcefile) {
var filename = jCls.@sourcefilename.toString() ?: new File(jCls.@name.toString()).name
var allJLines = sourcefile.findAll({ it.@name.toString().startsWith(filename) }).line.toList()
String jClsName = jCls.@name.toString()
var cls = parent.appendNode('class', [
name : jClsName.replaceAll('/', '.'),
filename: jClsName.substring(0, jClsName.lastIndexOf('/') + 1) + filename
] + calcCounters(jCls))
var methods = cls.appendNode('methods')
var jMethods = jCls.method
jMethods.each {
var method = methods.appendNode('method', [name: it.@name.toString(), signature: it.@desc.toString()] + calcCounters(it))
convertLines(method, methodLines(it, jMethods, allJLines))
}
convertLines(cls, allJLines)
return cls
}
private static void convertPackage(Node parent, GPathResult jPackage) {
var pkg = parent.appendNode('package', [name: jPackage.'@name'.toString().replaceAll('/', '.')] + calcCounters(jPackage))
var clss = pkg.appendNode('classes')
jPackage.class.each { convertClass(clss, it, jPackage.sourcefile) }
}
private static Node convertRoot(GPathResult source, Collection<String> sourceRoots) {
var root = new Node(null, 'coverage', [
timestamp: TimeUnit.MILLISECONDS.toSeconds(source?.sessioninfo?.@start?.first()?.toLong() ?: System.currentTimeMillis())
] + calcCounters(source))
var sources = root.appendNode('sources')
sourceRoots.each { sources.appendNode('source', it) }
var packages = root.appendNode('packages')
source.group.package.each { convertPackage(packages, it) }
source.package.each { convertPackage(packages, it) }
return root
}
static void jacoco2cobertura(Writer writer, File file, Set<File> sourceRoots) {
var root = new XmlSlurper().with(true) {
setFeature('http://apache.org/xml/features/disallow-doctype-decl', false)
setFeature('http://apache.org/xml/features/nonvalidating/load-external-dtd', false)
}.parse(file)
writer.append("""<?xml version="1.0" ?>\n""")
var nodePrinter = new XmlNodePrinter(new PrintWriter(writer))
nodePrinter.print(convertRoot(root, Arrays.asList(sourceRoots.path)))
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment