Skip to content

Instantly share code, notes, and snippets.

@bademux
Last active April 16, 2024 19:03
Show Gist options
  • Save bademux/5210f14fc89b43a99da6fcfe4ad7a62f to your computer and use it in GitHub Desktop.
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 = [email protected]()
int endLine = [email protected]({ it.toInteger() }).findAll({ it > startLine }).min() ?: Integer.MAX_VALUE
return jLines.findAll {
var nr = [email protected]()
startLine <= nr && nr < endLine
}
}
private static void convertLines(Node parent, List<NodeChild> jLines) {
var lines = parent.appendNode('lines')
for (line in jLines) {
int mb = [email protected]()
int cb = [email protected]()
int ci = [email protected]()
boolean isBranch = mb + cb > 0
var l = lines.appendNode('line', [branch: isBranch.toString(), number: [email protected](), 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([email protected](), [email protected]()) : 0
}
private static Node convertClass(Node parent, NodeChild jCls, GPathResult sourcefile) {
var filename = [email protected]() ?: new File([email protected]()).name
var allJLines = sourcefile.findAll({ [email protected]().startsWith(filename) }).line.toList()
String jClsName = [email protected]()
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: [email protected](), signature: [email protected]()] + 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