Last active
April 16, 2024 19:03
-
-
Save bademux/5210f14fc89b43a99da6fcfe4ad7a62f to your computer and use it in GitHub Desktop.
Pure groovy jacoco2cobertura converter to be used with Gradle
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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