mirror of
https://github.com/php/doc-gtk.git
synced 2026-03-24 17:12:18 +01:00
467 lines
14 KiB
PHP
467 lines
14 KiB
PHP
<?php
|
|
/**
|
|
* PHP-Gtk coverage analysis generator.
|
|
*
|
|
* The script is based on the php-gtk-doc coverage
|
|
* analysis script with a slightly modified output.
|
|
*
|
|
* The main work is done in the Log2Coverage class
|
|
* that takes some gen_*.log files and transforms them
|
|
* into an xml file similar to the one generated by
|
|
* the coverage.xsl script for the documentation.
|
|
*
|
|
* Usage:
|
|
* Pass the paths of the gen_*.log files to the script, e.g.
|
|
* php coverage.php ext/* /gen_*.log > classcoverage.htm
|
|
* ^ remove this space
|
|
*
|
|
* @author Christian Weiske <cweiske@php.net>
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
* Converts a gen_* logfile (e.g. ext/gtk+/gen_gtk.log) to
|
|
* a coverage xml file as generated by the
|
|
* php-gtk-doc coverage.xsl script.
|
|
*/
|
|
class Log2Coverage
|
|
{
|
|
protected static $classpattern = "/([A-Za-z0-9]+)\n~+\n((?: .*\n)+)/";
|
|
protected static $funcpattern = "/ ([a-z ]+?)\s+ \\(([0-9]+) written, ([0-9]+) skipped\\)\n/";
|
|
protected static $notgenpattern = "/Not Generated Items\\s*\n=+\n((?: .+\n)*)\n/";
|
|
protected static $notgenline = "/ ([a-z ]+)\s+(([a-zA-Z0-9]+)(?:::|->)([a-zA-Z0-9_]+):.+)/";
|
|
|
|
/**
|
|
* I know that hardcoded titles are bad, but if
|
|
* the file isn't in here the gen_*.log filename
|
|
* is shown as fallback.
|
|
*/
|
|
protected static $filetitles = array(
|
|
'gen_gtk.log' => 'Gtk',
|
|
'gen_gdk.log' => 'Gdk',
|
|
'gen_atk.log' => 'Atk',
|
|
'gen_pango.log' => 'Pango',
|
|
'gen_libglade.log' => 'Glade',
|
|
'gen_mozembed.log' => 'GtkMozEmbed',
|
|
'gen_scintilla.log' => 'GtkScintilla',
|
|
'gen_sourceview.log' => 'GtkSourceview',
|
|
);
|
|
|
|
|
|
public static function generateFromArgs()
|
|
{
|
|
$files = $GLOBALS['argv'];
|
|
array_shift($files);
|
|
|
|
return self::generate($files);
|
|
}//public static function generateFromArgs()
|
|
|
|
|
|
|
|
public static function generate($files)
|
|
{
|
|
if (count($files) == 0) {
|
|
echo "I guess you forgot to specify the gen_*.log files to parse.\n";
|
|
}
|
|
|
|
$content = '<' . '?xml version="1.0" encoding="utf-8"?' . ">\n";
|
|
$content .= '<classcoverage>';
|
|
foreach ($files as $file) {
|
|
$content .= self::splitFile($file);
|
|
}
|
|
$content .= '</classcoverage>';
|
|
|
|
return $content;
|
|
}//public static function generate($files)
|
|
|
|
|
|
|
|
protected static function splitFile($filename)
|
|
{
|
|
//match not generated items
|
|
$matches = array();
|
|
preg_match_all(self::$notgenpattern, file_get_contents($filename), $matches);
|
|
$notgen = $matches[1][0];
|
|
$arNotGen = self::splitNotGenerated($notgen);
|
|
|
|
//match statistics
|
|
$matches = array();
|
|
preg_match_all(self::$classpattern, file_get_contents($filename), $matches);
|
|
|
|
$bn = basename($filename);
|
|
$title = isset(self::$filetitles[$bn]) ? self::$filetitles[$bn] : $bn;
|
|
|
|
$content = '<classset title="' . $title . '">';
|
|
$contents = array();
|
|
foreach ($matches[1] as $id => $class) {
|
|
$notgen = isset($arNotGen[$class]) ? $arNotGen[$class] : array();
|
|
$contents[$class] = self::splitClass($class, $matches[2][$id], $notgen);
|
|
}
|
|
ksort($contents);
|
|
$content .= implode('', $contents);
|
|
$content .= '</classset>';
|
|
|
|
return $content;
|
|
}//protected static function splitFile($filename)
|
|
|
|
|
|
|
|
protected static function splitClass($class, $data, $notgen)
|
|
{
|
|
$matches = array();
|
|
preg_match_all(self::$funcpattern, $data, $matches);
|
|
|
|
$content = '<class title="' . $class . '">';
|
|
foreach ($matches[1] as $id => $name) {
|
|
$written = $matches[2][$id];
|
|
$skipped = $matches[3][$id];
|
|
if ($name[0] == 'p') {
|
|
$name = 'properties';
|
|
} else if ($name[0] == 'f') {
|
|
$name = 'methods';
|
|
}
|
|
$content .= '<type title="' . $name . '"'
|
|
. ' existing="' . ($written + $skipped) . '"'
|
|
. ' missing="' . ($skipped) . '"';
|
|
|
|
if (!isset($notgen[$name])) {
|
|
$content .= '/>';
|
|
} else {
|
|
$content .= '>';
|
|
foreach ($notgen[$name] as $name => $message) {
|
|
$content .= '<missing'
|
|
. ' name="' . htmlspecialchars($name) . '"'
|
|
. ' message="' . htmlspecialchars($message) . '"'
|
|
. '/>';
|
|
}
|
|
$content .= '</type>';
|
|
}
|
|
}
|
|
$content .= '</class>';
|
|
|
|
return $content;
|
|
}//protected static function splitClass($class, $data)
|
|
|
|
|
|
|
|
protected static function splitNotGenerated($notgen)
|
|
{
|
|
$ar = explode("\n", $notgen);
|
|
$arNotGen = array();
|
|
foreach ($ar as $line) {
|
|
$matches = array();
|
|
preg_match_all(self::$notgenline, $line, $matches);
|
|
if (!isset($matches[0][0])) {
|
|
//strange...
|
|
continue;
|
|
}
|
|
$type = trim($matches[1][0]) . 's';
|
|
$message = $matches[2][0];
|
|
$class = $matches[3][0];
|
|
$name = $matches[4][0];
|
|
|
|
if ($type[0] == 'r') {//reader for
|
|
$type = 'properties';
|
|
} else if ($type[0] == 'f') {//function
|
|
$type = 'methods';
|
|
}
|
|
|
|
$arNotGen[$class][$type][$name] = $message;
|
|
}
|
|
return $arNotGen;
|
|
}//protected static function splitNotGenerated($notgen)
|
|
|
|
}//class Log2Coverage
|
|
|
|
|
|
|
|
/**
|
|
* PHP-Gtk generator coverage analysis generator
|
|
*
|
|
* Based on the php-gtk-doc coverage generator.
|
|
*
|
|
* @author Christian Weiske <cweiske@php.net>
|
|
*/
|
|
class ClassCoverageAnalysis
|
|
{
|
|
protected $typestemplate = array(
|
|
'constructors' => array(
|
|
'missing' => 0,
|
|
'existing' => 0
|
|
),
|
|
'methods' => array(
|
|
'missing' => 0,
|
|
'existing' => 0
|
|
),
|
|
'properties' => array(
|
|
'missing' => 0,
|
|
'existing' => 0
|
|
),
|
|
);
|
|
|
|
protected $missingThings = '';
|
|
|
|
|
|
public function run()
|
|
{
|
|
$doc = simplexml_load_string(Log2Coverage::generateFromArgs());
|
|
|
|
$output = <<<EOD
|
|
<html>
|
|
<head>
|
|
<title>PHP-Gtk2 generator coverage analysis</title>
|
|
<style type="text/css">
|
|
table {
|
|
border: 1px solid black;
|
|
border-collapse: collapse;
|
|
}
|
|
td, th {
|
|
border: 1px solid grey;
|
|
}
|
|
table tr.classset td, table tr.documentation td {
|
|
font-weight:bold;
|
|
border-bottom: 2px solid black;
|
|
}
|
|
th.allpercent {
|
|
font-size: 200%;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<table>
|
|
<caption>
|
|
EOD;
|
|
$output .= 'PHP-Gtk2 generator coverage analysis of ' . date('Y-m-d H:i:s');
|
|
$output .= <<<EOD
|
|
</caption>
|
|
EOD;
|
|
$output .= $this->calcCoverage($doc);
|
|
$output .= <<<EOD
|
|
</table>
|
|
EOD;
|
|
$output .= $this->missingThings;
|
|
$output .= <<<EOD
|
|
</body>
|
|
</html>
|
|
EOD;
|
|
return $output;
|
|
}//public function run()
|
|
|
|
|
|
|
|
protected function calcCoverage($doc)
|
|
{
|
|
$output = '<thead>' . "\r\n"
|
|
. '<tr>'
|
|
. '<th rowspan="2">Name</th>'
|
|
. '<th colspan="2">Constructors</th>'
|
|
. '<th colspan="2">Methods</th>'
|
|
. '<th colspan="2">Properties</th>'
|
|
. '</tr>' . "\r\n"
|
|
. '<tr>'
|
|
. '<th>Done</th><th>All</th>'
|
|
. '<th>Done</th><th>All</th>'
|
|
. '<th>Done</th><th>All</th>'
|
|
. '</tr>' . "\r\n"
|
|
. '</thead>' . "\r\n"
|
|
. '<tbody>' . "\r\n";
|
|
|
|
$allMissing = 0;
|
|
$allExisting = 0;
|
|
$types = $this->typestemplate;
|
|
foreach ($doc->classset as $classset) {
|
|
list($classoutput, $existing, $missing, $settypes) = $this->calcClassset($classset);
|
|
$types = $this->addTypes($types, $settypes);
|
|
$allMissing += $missing;
|
|
$allExisting += $existing;
|
|
$output .= $classoutput;
|
|
}
|
|
|
|
$output .= '<tr class="documentation">'
|
|
. '<th rowspan="2">Coverage</th>'
|
|
. $this->getTypesDisplay($types, null)
|
|
. '</tr>'
|
|
. '<tr class="documentation">'
|
|
. $this->getTypesPercentageDisplay($types)
|
|
. '</tr>'
|
|
. "\r\n";
|
|
|
|
$percent = 100 / $allExisting * ($allExisting - $allMissing);
|
|
$output .= '<tr><th>All in all</th>'
|
|
. '<th>' . ($allExisting - $allMissing) . '</th>'
|
|
. '<th>' . $allExisting . '</th>'
|
|
. '<th class="allpercent" colspan="4" style="background-color:' . $this->getColor($percent) . '">'
|
|
. number_format($percent, 2)
|
|
. '%</th>'
|
|
. '</tr>' . "\r\n"
|
|
. '<tbody>' . "\r\n";
|
|
|
|
return $output;
|
|
}//protected function calcCoverage($doc)
|
|
|
|
|
|
|
|
public function calcClassset($classset)
|
|
{
|
|
$output = '';
|
|
$allMissing = 0;
|
|
$allExisting = 0;
|
|
$types = $this->typestemplate;
|
|
foreach ($classset->{'class'} as $class) {
|
|
list($classoutput, $existing, $missing, $classtypes) = $this->calcClass($class);
|
|
$types = $this->addTypes($types, $classtypes);
|
|
$allMissing += $missing;
|
|
$allExisting += $existing;
|
|
$output .= $classoutput;
|
|
}
|
|
|
|
$output .= '<tr class="classset">'
|
|
. '<th rowspan="2">' . htmlspecialchars((string)$classset['title']) . '</th>'
|
|
. $this->getTypesDisplay($types, null)
|
|
. '</tr>'
|
|
. '<tr class="classset">'
|
|
. $this->getTypesPercentageDisplay($types)
|
|
. '</tr>'
|
|
. "\r\n";
|
|
|
|
return array($output, $allExisting, $allMissing, $types);
|
|
}
|
|
|
|
|
|
|
|
public function calcClass($class)
|
|
{
|
|
$classname = htmlspecialchars((string)$class['title']);
|
|
$types = $this->typestemplate;
|
|
$allMissing = 0;
|
|
$allExisting = 0;
|
|
if (isset($class->type)) {
|
|
foreach ($class->type as $type) {
|
|
$title = (string)$type['title'];
|
|
$types[$title]['missing'] = (string)$type['missing'];
|
|
$types[$title]['existing'] = (string)$type['existing'];
|
|
$allMissing += $types[$title]['missing'];
|
|
$allExisting += $types[$title]['existing'];
|
|
|
|
if (isset($type->missing)) {
|
|
$this->missingThings .= '<a'
|
|
. ' name="' . $classname . '-' . $title . '">'
|
|
. '<h3>' . $classname . ' ' . $title . '</h3>'
|
|
. '</a>';
|
|
foreach ($type->missing as $missing) {
|
|
$name = (string)$missing['name'];
|
|
$message = (string)$missing['message'];
|
|
$this->missingThings .= htmlspecialchars($message) . '<br/>';
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
$output = '<tr><td>' . $classname . '</td>'
|
|
. $this->getTypesDisplay($types, $classname)
|
|
. '</tr>' . "\r\n";
|
|
|
|
return array($output, $allExisting, $allMissing, $types);
|
|
}//public function calcClass($class)
|
|
|
|
|
|
|
|
function getTypesDisplay($types, $class)
|
|
{
|
|
return $this->getTypeDisplay($types['constructors'], $class, 'constructors')
|
|
. $this->getTypeDisplay($types['methods'] , $class, 'methods')
|
|
. $this->getTypeDisplay($types['properties'] , $class, 'properties');
|
|
}//function getTypesDisplay($types, $class)
|
|
|
|
|
|
|
|
function getTypesPercentageDisplay($types)
|
|
{
|
|
return $this->getTypePercentageDisplay($types['constructors'])
|
|
. $this->getTypePercentageDisplay($types['methods'])
|
|
. $this->getTypePercentageDisplay($types['properties']);
|
|
}//function getTypesPercentageDisplay($types)
|
|
|
|
|
|
|
|
function getTypeDisplay($type, $class, $typename)
|
|
{
|
|
$existing = $type['existing'];
|
|
$missing = $type['missing'];
|
|
$done = $existing - $missing;
|
|
if ($existing != 0) {
|
|
$percent = intval(100 / $existing * $done);
|
|
} else {
|
|
$percent = -100;
|
|
}
|
|
$color = $this->getColor($percent);
|
|
|
|
if ($missing > 0 && $class !== null) {
|
|
$done = '<a href="#' . $class . '-' . $typename . '">' . $done . '</a>';
|
|
}
|
|
|
|
$output = '<td style="background-color:' . $color . '">' . $done . '</td>';
|
|
$output .= '<td>' . $existing . '</td>';
|
|
return $output;
|
|
}//function getTypeDisplay($type)
|
|
|
|
|
|
|
|
function getTypePercentageDisplay($type)
|
|
{
|
|
$existing = $type['existing'];
|
|
$missing = $type['missing'];
|
|
$done = $existing - $missing;
|
|
if ($existing != 0) {
|
|
$percent = number_format(100 / $existing * $done, 2);
|
|
} else {
|
|
$percent = -100;
|
|
}
|
|
$color = $this->getColor($percent);
|
|
|
|
if ($percent == -100) {
|
|
$percent = '';
|
|
} else {
|
|
$percent .= '%';
|
|
}
|
|
$output = '<td colspan="2" style="background-color:' . $color . '">' . $percent . '</td>';
|
|
return $output;
|
|
}//function getTypePercentageDisplay($type)
|
|
|
|
|
|
|
|
function getColor($percent)
|
|
{
|
|
$percent = intval($percent);
|
|
if ($percent == -100) {
|
|
$color = 'white';
|
|
} else if ($percent == 100) {
|
|
$color = 'green';
|
|
} else if ($percent >= 85) {
|
|
$color = 'yellow';
|
|
} else if ($percent >= 50) {
|
|
$color = 'orange';
|
|
} else {
|
|
$color = 'red';
|
|
}
|
|
return $color;
|
|
}//function getColor($percent)
|
|
|
|
|
|
|
|
protected function addTypes($types, $newTypes)
|
|
{
|
|
foreach ($newTypes as $name => $numbers) {
|
|
foreach ($numbers as $numtitle => $number) {
|
|
$types[$name][$numtitle] += $number;
|
|
}
|
|
}
|
|
return $types;
|
|
}//protected function addTypes($types, $newTypes)
|
|
|
|
}//class ClassCoverageAnalysis
|
|
|
|
$da = new ClassCoverageAnalysis();
|
|
echo $da->run();
|
|
?>
|