- https://github.com/d4rkr00t/badgs/tree/master/lib/templates
- https://github.com/marketplace/actions/jacoco-badge-generator
- https://www.december.com/html/spec/colorsvg.html
package coverage;
import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.script.Bindings;
import javax.script.ScriptContext;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import javax.script.SimpleBindings;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPathExpressionException;
import org.xml.sax.SAXException;
public class Coverage {
public static void generateBadge(String templateType) throws IOException, ScriptException,
XPathExpressionException, SAXException, ParserConfigurationException {
String badgesFolderName = "coverage";
String baseLocation = Paths.get(".").toAbsolutePath().normalize().toString();
HashMap<String, HashMap<String, Integer>> metrics =
getXmlValues(baseLocation + File.separator + "target/site/jacoco/jacoco.xml");
Class clazz = Coverage.class;
ClassLoader loader = clazz.getClassLoader();
String coverageClazzCompiledAbsoluteTargetLocation =
loader.getResource(clazz.getCanonicalName().replace(".", File.separator) + ".class")
.getFile().replace(File.separatorChar + "$", "");
String coverageFolderAbsoluteLocation =
new File(coverageClazzCompiledAbsoluteTargetLocation).getParent();
new File(baseLocation + File.separator + badgesFolderName).mkdirs();
String badgeTemplate = new String(
Files.readAllBytes(Paths
.get(coverageFolderAbsoluteLocation + File.separator + templateType + ".template")),
StandardCharsets.UTF_8);
for (Entry<String, HashMap<String, Integer>> metric : metrics.entrySet()) {
// String label, double percentage
String label = metric.getKey();
HashMap<String, Integer> metricDetails = metric.getValue();
double percentage = (metricDetails.get("covered") * 100)
/ (metricDetails.get("covered") + metricDetails.get("missed"));
String color = null;
if (percentage == 100d) {
color = "limegreen";
} else if (percentage > 80d && percentage <= 99.9d) {
color = "yellowgreen";
} else if (percentage > 70d && percentage <= 80d) {
color = "olivedrab";
} else if (percentage > 60d && percentage <= 70d) {
color = "goldenrod";
} else if (percentage > 50d && percentage <= 60d) {
color = "darkorange";
} else if (percentage <= 50d) {
color = "indianred";
}
ScriptEngineManager mgr = new ScriptEngineManager();
ScriptEngine engine = mgr.getEngineByName("JavaScript");
Bindings bindings = new SimpleBindings();
bindings.put("widthA", 70);
bindings.put("widthB", 50);
bindings.put("color", color);
bindings.put("subject", label);
bindings.put("status", percentage + "%");
engine.setBindings(bindings, ScriptContext.ENGINE_SCOPE);
String evaluatedContent = (String) engine.eval(badgeTemplate);
Files.write(
Paths.get(baseLocation + File.separator + badgesFolderName + File.separator + label + ".svg"),
evaluatedContent.getBytes());
}
}
public static HashMap<String, HashMap<String, Integer>> getXmlValues(String xmlAbsoluteLocation)
throws IOException {
String content = new String(Files.readAllBytes(Paths.get(xmlAbsoluteLocation)));
String lastRawPart = content.substring(content.lastIndexOf("</package>"));
Pattern pattern = Pattern.compile("<counter.+");
Pattern patternKeyValue = Pattern.compile("[a-z]+=\"[.\\S]+\"");
Matcher matcher = pattern.matcher(lastRawPart);
HashMap<String, HashMap<String, Integer>> metrics = new HashMap<>();
while (matcher.find()) {
String rawLine = matcher.group();
Matcher matcherKeyValue = patternKeyValue.matcher(rawLine);
HashMap<String, Integer> metric = new HashMap<>();
String type = null;
while (matcherKeyValue.find()) {
String rawKeyValue[] = matcherKeyValue.group().replaceAll("\"", "").split("=");
if (rawKeyValue[0].contentEquals("type")) {
type = rawKeyValue[1].toLowerCase();
} else {
metric.put(rawKeyValue[0], Integer.parseInt(rawKeyValue[1]));
}
}
metrics.put(type, metric);
}
return metrics;
}
public static void main(String[] args) throws IOException, ScriptException,
XPathExpressionException, SAXException, ParserConfigurationException {
generateBadge("flat");
}
}
flat template
'\n <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="' + (widthA + widthB) + '" height="20">\n <linearGradient id="smooth" x2="0" y2="100%">\n <stop offset="0" stop-color="#bbb" stop-opacity=".1"/>\n <stop offset="1" stop-opacity=".1"/>\n </linearGradient>\n\n <mask id="round">\n <rect width="' + (widthA + widthB) + '" height="20" rx="3" fill="#fff"/>\n </mask>\n\n <g mask="url(#round)">\n <rect width="' + widthA + '" height="20" fill="#555"/>\n <rect x="' + widthA + '" width="' + widthB + '" height="20" fill="' + (color || '#555') + '"/>\n <rect width="' + (widthA + widthB) + '" height="20" fill="url(#smooth)"/>\n </g>\n\n <g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">\n <text x="' + widthA / 2 + '" y="15" fill="#010101" fill-opacity=".3">' + subject + '</text>\n <text x="' + widthA / 2 + '" y="14">' + subject + '</text>\n <text x="' + (widthA + widthB / 2) + '" y="15" fill="#010101" fill-opacity=".3">' + status + '</text>\n <text x="' + (widthA + widthB / 2) + '" y="14">' + status + '</text>\n </g>\n </svg>'