Files
JIMRI/scripts/jacoco_sort_report.php
2026-06-17 14:00:51 +02:00

319 lines
9.6 KiB
PHP

<?php
// This script reads the Jacoco Coverage report and sort it by number of missing lines.
// Author: Daniel Bergqvist
// See the parallel version in the scripts/ directory of a JMRI/JMRI checkout
// Set to true to show the DOM tree instead of the html page.
// Useful if the original Jacoco page is changed and the script
// needs to be changed.
$showDOMTree = false;
// Print some information to the screen
$printDebugOutput = false;
// Print the HTML page
$printHTMLPage = true;
error_reporting(E_ERROR | E_WARNING | E_PARSE | E_NOTICE);
ini_set("display_errors", 1);
// https://stackoverflow.com/questions/10524300/php-domdocument-loadhtml-error
// In many cases it is advisable to use libxml_use_internal_errors(true); before $dom->loadHTML($content);
libxml_use_internal_errors(true);
$url = 'https://builds.jmri.org/jenkins/job/Development/job/JaCoCo/lastStableBuild/jacoco/';
// Is the script run from the command line?
if (isset($argc) && ($argc > 0)) {
if ($argc == 2) { // Package list
parse_page($argv[1], '', false);
} elseif ($argc == 3) { // Class list
parse_page($argv[1], $argv[2], true);
} else { // Wrong number of arguments
echo "\n";
echo "jacoco_sort_report.php <filename>\n";
echo "jacoco_sort_report.php <filename> <package>\n";
echo "\n";
echo "Examples:\n";
echo "jacoco_sort_report.php index.html\n";
echo "jacoco_sort_report.php apps.DecoderPro/index.html apps.DecoderPro\n";
echo "\n";
}
} elseif (isset($_REQUEST['package'])) {
$packageName = $_REQUEST['package'];
if (preg_match('/^(\w+\.)*\w+$/', $packageName) == 1) {
parse_page($url.$packageName, $packageName, true);
} else {
echo "Invalid package name. Only characters and dots are allowed in a package name.<br/>\n";
}
} else {
parse_page($url, '', false);
}
exit;
function getNodeType($nodeType)
{
switch ($nodeType)
{
case XML_ELEMENT_NODE: return 'XML_ELEMENT_NODE';
case XML_ATTRIBUTE_NODE: return 'XML_ATTRIBUTE_NODE';
case XML_TEXT_NODE: return 'XML_TEXT_NODE';
case XML_CDATA_SECTION_NODE: return 'XML_CDATA_SECTION_NODE';
case XML_ENTITY_REF_NODE: return 'XML_ENTITY_REF_NODE';
case XML_ENTITY_NODE: return 'XML_ENTITY_NODE';
case XML_PI_NODE: return 'XML_PI_NODE';
case XML_COMMENT_NODE: return 'XML_COMMENT_NODE';
case XML_DOCUMENT_NODE: return 'XML_DOCUMENT_NODE';
case XML_DOCUMENT_TYPE_NODE: return 'XML_DOCUMENT_TYPE_NODE';
case XML_DOCUMENT_FRAG_NODE: return 'XML_DOCUMENT_FRAG_NODE';
case XML_NOTATION_NODE: return 'XML_NOTATION_NODE';
case XML_HTML_DOCUMENT_NODE: return 'XML_HTML_DOCUMENT_NODE';
case XML_DTD_NODE: return 'XML_DTD_NODE';
case XML_ELEMENT_DECL_NODE: return 'XML_ELEMENT_DECL_NODE';
case XML_ATTRIBUTE_DECL_NODE: return 'XML_ATTRIBUTE_DECL_NODE';
case XML_ENTITY_DECL_NODE: return 'XML_ENTITY_DECL_NODE';
case XML_NAMESPACE_DECL_NODE: return 'XML_NAMESPACE_DECL_NODE';
case XML_ATTRIBUTE_CDATA: return 'XML_ATTRIBUTE_CDATA';
case XML_ATTRIBUTE_ID: return 'XML_ATTRIBUTE_ID';
case XML_ATTRIBUTE_IDREF: return 'XML_ATTRIBUTE_IDREF';
case XML_ATTRIBUTE_IDREFS: return 'XML_ATTRIBUTE_IDREFS';
case XML_ATTRIBUTE_ENTITY: return 'XML_ATTRIBUTE_ENTITY';
case XML_ATTRIBUTE_NMTOKEN: return 'XML_ATTRIBUTE_NMTOKEN';
case XML_ATTRIBUTE_NMTOKENS: return 'XML_ATTRIBUTE_NMTOKENS';
case XML_ATTRIBUTE_ENUMERATION: return 'XML_ATTRIBUTE_ENUMERATION';
case XML_ATTRIBUTE_NOTATION: return 'XML_ATTRIBUTE_NOTATION';
default: return "Unknown node type";
}
}
function show_tree($pad, $id, $element)
{
if (isset($element->tagName) && in_array($element->tagName, array("link", "script")))
return;
if ($element->nodeType == XML_TEXT_NODE) {
echo $pad."Text node: '".$element->nodeValue."'<br>\n";
}
else {
$attr = "";
if (is_object($element->attributes))
{
for ($i = 0; $i < $element->attributes->length; $i++)
{
$attr .= $element->attributes->item($i)->name . ": " . $element->attributes->item($i)->value . ", ";
}
}
if (isset($element->tagName))
echo $pad.$id.": &lt;".$element->tagName."&gt;&nbsp;&nbsp;&nbsp;" . $attr . "<br />\n";
else
echo $pad."Tag has no name. Type: ".$element->nodeType."<br>\n";
if (is_object($element->childNodes))
{
for ($i = 0; $i < $element->childNodes->length; $i++)
{
$child = $element->childNodes->item($i);
show_tree($pad."&nbsp;&nbsp;&nbsp;", $id.'.'.$i, $child);
}
}
else
{
echo "\$element->childNodes is not an object. ".getNodeType($element->nodeType)."<br>\n";
}
}
}
class CoverageInfo {
public $element;
public $name;
public $numMissedInstructions;
public $numMissedBranches;
public $numMissedComplexities;
public $numMissedLines;
public $numMissedMethods;
public $numMissedClasses;
public $numMissedLinesTotal;
}
function compare($a, $b)
{
if ($a->numMissedLines == $b->numMissedLines) {
return 0;
}
return ($a->numMissedLines > $b->numMissedLines) ? -1 : 1;
}
function parse_page_prim($page, $url, $package, $isClassList)
{
global $showDOMTree;
global $printDebugOutput;
global $printHTMLPage;
$page = trim($page);
$DOM = new DOMDocument;
$DOM->loadHTML($page);
// Show the DOM tree?
if ($showDOMTree) {
show_tree("", '0', $DOM->documentElement);
} else {
$elements = $DOM->documentElement->getElementsByTagName('link');
foreach ($elements as $element) {
if ($element->hasAttribute('href')) {
$href = $element->getAttribute('href');
$element->setAttribute('href', 'https://builds.jmri.org'.$href);
}
}
$elements = $DOM->documentElement->getElementsByTagName('script');
foreach ($elements as $element) {
$element->parentNode->removeChild($element);
}
$elements = $DOM->documentElement->getElementsByTagName('img');
foreach ($elements as $element) {
if ($element->hasAttribute('src')) {
$src = $element->getAttribute('src');
$element->setAttribute('src', 'https://builds.jmri.org'.$src);
}
}
$bodyElement = $DOM->documentElement->childNodes->item(1);
if ($isClassList) { // Class list
$tableElement = $bodyElement->childNodes->item(2)->childNodes->item(1)->childNodes->item(6);
} else { // Package list
$tableElement = $bodyElement->childNodes->item(2)->childNodes->item(1)->childNodes->item(7);
}
$coverageInfoArray = array();
for ($i=1; $i < $tableElement->childNodes->length; $i++) {
$tableRowElement = $tableElement->childNodes->item($i);
$coverageInfo = new CoverageInfo;
$coverageInfo->element = $tableRowElement;
$elements = $tableRowElement->getElementsByTagName('a');
foreach ($elements as $element) {
if ($element->hasAttribute('href')) {
// The A-tag has the name of the package or class
$coverageInfo->name = $element->nodeValue;
if ($isClassList) { // Class list
$href = $element->getAttribute('href');
$element->setAttribute('href', 'https://builds.jmri.org/jenkins/job/Development/job/JaCoCo/lastStableBuild/jacoco/'.$package.'/'.$href);
} else { // Package list
$href = $element->nodeValue;
$element->setAttribute('href', 'jacoco_sort_report.php?package='.$href);
}
}
}
$aElement = $tableRowElement->childNodes->item(0)->childNodes->item(0);
$instructionElement = $tableRowElement->childNodes->item(1)->childNodes->item(1)->childNodes->item(0)->childNodes->item(0)->childNodes->item(0);
$instructionArray = explode(" ", $instructionElement->nodeValue);
$coverageInfo->numMissedInstructions = $instructionArray[1];
$branchElement = $tableRowElement->childNodes->item(3)->childNodes->item(1)->childNodes->item(0)->childNodes->item(0)->childNodes->item(0);
$branchArray = explode(" ", $branchElement->nodeValue);
$coverageInfo->numMissedBranches = $branchArray[1];
$complexityElement = $tableRowElement->childNodes->item(5)->childNodes->item(1)->childNodes->item(0)->childNodes->item(0)->childNodes->item(0);
$complexityArray = explode(" ", $complexityElement->nodeValue);
$coverageInfo->numMissedComplexities = $complexityArray[1];
$linesElement = $tableRowElement->childNodes->item(7)->childNodes->item(1)->childNodes->item(0)->childNodes->item(0)->childNodes->item(0);
$linesArray = explode(" ", $linesElement->nodeValue);
$coverageInfo->numMissedLines = $linesArray[1];
$methodElement = $tableRowElement->childNodes->item(9)->childNodes->item(1)->childNodes->item(0)->childNodes->item(0)->childNodes->item(0);
$methodArray = explode(" ", $methodElement->nodeValue);
$coverageInfo->numMissedMethods = $methodArray[1];
$classElement = $tableRowElement->childNodes->item(11)->childNodes->item(1)->childNodes->item(0)->childNodes->item(0)->childNodes->item(0);
$classArray = explode(" ", $classElement->nodeValue);
$coverageInfo->numMissedClasses = $classArray[1];
$coverageInfo->numMissedLinesTotal = $coverageInfo->numMissedLines;
$coverageInfoArray[] = $coverageInfo;
if ($printDebugOutput) {
echo "Package: " . $aElement->childNodes->item(0)->nodeValue
. ", instr: " . $coverageInfo->numMissedInstructions
. ", branch: " . $coverageInfo->numMissedBranches
. ", compl: " . $coverageInfo->numMissedComplexities
. ", lines: " . $coverageInfo->numMissedLines
. ", method: " . $coverageInfo->numMissedMethods
. ", class: " . $coverageInfo->numMissedClasses
. "<br />";
}
}
while ($tableElement->childNodes->length > 1) {
$tableElement->removeChild($tableElement->childNodes->item(1));
}
usort($coverageInfoArray, "compare");
foreach ($coverageInfoArray as $coverageInfo) {
$tableElement->appendChild($coverageInfo->element);
}
if ($printHTMLPage) {
echo $DOM->saveHTML();
}
}
}
function parse_page($url, $package, $isClassList)
{
$page = file_get_contents($url);
parse_page_prim($page, $url, $package, $isClassList);
}
?>