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 = 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