cvs: php-gtk-doc /scripts classcoverage.php

[email protected] ("Christian Weiske") Wed, 22 Nov 2006 17:27:57 -0000
Newsgroups php.gtk.doc
Message-ID <cvscweiske1164216477@cvsserver>
cweiske		Wed Nov 22 17:27:57 2006 UTC

  Added files:                 
    /php-gtk-doc/scripts	classcoverage.php 
  Log:
  Add classcoverage script to CVS after it was lost on my disk
  and I luckily found it on an old backup
cweiske-20061122172757.txt (text/plain, 13.9 KB)
http://cvs.php.net/viewvc.cgi/php-gtk-doc/scripts/classcoverage.php?view=markup&rev=1.1
Index: php-gtk-doc/scripts/classcoverage.php
+++ php-gtk-doc/scripts/classcoverage.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 <[email protected]>
*/



/**
*   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_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 <[email protected]>
*/
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();
?>