home *** CD-ROM | disk | FTP | other *** search
/ Cricao de Sites - 650 Layouts Prontos / WebMasters.iso / Servidores / xampp-win32-1.6.7-installer.exe / php / PEAR / phing / tasks / ext / coverage / CoverageReportTask.php < prev    next >
Encoding:
PHP Script  |  2007-08-14  |  10.9 KB  |  419 lines

  1. <?php
  2. /**
  3.  * $Id: CoverageReportTask.php 216 2007-08-14 14:17:15Z mrook $
  4.  *
  5.  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  6.  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  7.  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  8.  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  9.  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  10.  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  11.  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  12.  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  13.  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  14.  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  15.  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  16.  *
  17.  * This software consists of voluntary contributions made by many individuals
  18.  * and is licensed under the LGPL. For more information please see
  19.  * <http://phing.info>.
  20.  */
  21.  
  22. require_once 'phing/Task.php';
  23. require_once 'phing/system/io/PhingFile.php';
  24. require_once 'phing/system/io/Writer.php';
  25. require_once 'phing/system/util/Properties.php';
  26. require_once 'phing/tasks/ext/phpunit/PHPUnitUtil.php';
  27. require_once 'phing/tasks/ext/coverage/CoverageReportTransformer.php';
  28.  
  29. /**
  30.  * Transforms information in a code coverage database to XML
  31.  *
  32.  * @author Michiel Rook <michiel.rook@gmail.com>
  33.  * @version $Id: CoverageReportTask.php 216 2007-08-14 14:17:15Z mrook $
  34.  * @package phing.tasks.ext.coverage
  35.  * @since 2.1.0
  36.  */
  37. class CoverageReportTask extends Task
  38. {
  39.     private $outfile = "coverage.xml";
  40.  
  41.     private $transformers = array();
  42.  
  43.     /** the classpath to use (optional) */
  44.     private $classpath = NULL;
  45.     
  46.     /** the path to the GeSHi library (optional) */
  47.     private $geshipath = "";
  48.     
  49.     /** the path to the GeSHi language files (optional) */
  50.     private $geshilanguagespath = "";
  51.     
  52.     function setClasspath(Path $classpath)
  53.     {
  54.         if ($this->classpath === null)
  55.         {
  56.             $this->classpath = $classpath;
  57.         }
  58.         else
  59.         {
  60.             $this->classpath->append($classpath);
  61.         }
  62.     }
  63.  
  64.     function createClasspath()
  65.     {
  66.         $this->classpath = new Path();
  67.         return $this->classpath;
  68.     }
  69.     
  70.     function setGeshiPath($path)
  71.     {
  72.         $this->geshipath = $path;
  73.     }
  74.  
  75.     function setGeshiLanguagesPath($path)
  76.     {
  77.         $this->geshilanguagespath = $path;
  78.     }
  79.  
  80.     function __construct()
  81.     {
  82.         $this->doc = new DOMDocument();
  83.         $this->doc->encoding = 'UTF-8';
  84.         $this->doc->formatOutput = true;
  85.         $this->doc->appendChild($this->doc->createElement('snapshot'));
  86.     }
  87.  
  88.     function setOutfile($outfile)
  89.     {
  90.         $this->outfile = $outfile;
  91.     }
  92.  
  93.     /**
  94.      * Generate a report based on the XML created by this task
  95.      */
  96.     function createReport()
  97.     {
  98.         $transformer = new CoverageReportTransformer($this);
  99.         $this->transformers[] = $transformer;
  100.         return $transformer;
  101.     }
  102.  
  103.     protected function getPackageElement($packageName)
  104.     {
  105.         $packages = $this->doc->documentElement->getElementsByTagName('package');
  106.  
  107.         foreach ($packages as $package)
  108.         {
  109.             if ($package->getAttribute('name') == $packageName)
  110.             {
  111.                 return $package;
  112.             }
  113.         }
  114.  
  115.         return NULL;
  116.     }
  117.  
  118.     protected function addClassToPackage($classname, $element)
  119.     {
  120.         $packageName = PHPUnitUtil::getPackageName($classname);
  121.  
  122.         $package = $this->getPackageElement($packageName);
  123.  
  124.         if ($package === NULL)
  125.         {
  126.             $package = $this->doc->createElement('package');
  127.             $package->setAttribute('name', $packageName);
  128.             $this->doc->documentElement->appendChild($package);
  129.         }
  130.  
  131.         $package->appendChild($element);
  132.     }
  133.  
  134.     protected function stripDiv($source)
  135.     {
  136.         $openpos = strpos($source, "<div");
  137.         $closepos = strpos($source, ">", $openpos);
  138.  
  139.         $line = substr($source, $closepos + 1);
  140.  
  141.         $tagclosepos = strpos($line, "</div>");
  142.  
  143.         $line = substr($line, 0, $tagclosepos);
  144.  
  145.         return $line;
  146.     }
  147.  
  148.     protected function highlightSourceFile($filename)
  149.     {
  150.         if ($this->geshipath)
  151.         {
  152.             require_once $this->geshipath . '/geshi.php';
  153.             
  154.             $source = file_get_contents($filename);
  155.  
  156.             $geshi = new GeSHi($source, 'php', $this->geshilanguagespath);
  157.  
  158.             $geshi->enable_line_numbers(GESHI_NORMAL_LINE_NUMBERS);
  159.  
  160.             $geshi->enable_strict_mode(true);
  161.  
  162.             $geshi->enable_classes(true);
  163.  
  164.             $geshi->set_url_for_keyword_group(3, ''); 
  165.  
  166.             $html = $geshi->parse_code();
  167.  
  168.             $lines = split("<li>|</li>", $html);
  169.  
  170.             // skip first and last line
  171.             array_pop($lines);
  172.             array_shift($lines);
  173.  
  174.             $lines = array_filter($lines);
  175.  
  176.             $lines = array_map(array($this, 'stripDiv'), $lines);
  177.  
  178.             return $lines;
  179.         }
  180.         else
  181.         {
  182.             $lines = file($filename);
  183.             
  184.             for ($i = 0; $i < count($lines); $i++)
  185.             {
  186.                 $line = $lines[$i];
  187.                 
  188.                 $line = rtrim($line);
  189.                 
  190.                 if (function_exists('mb_convert_encoding'))
  191.                 {
  192.                     $lines[$i] = mb_convert_encoding($line, 'UTF-8');
  193.                 }
  194.                 else
  195.                 {
  196.                     $lines[$i] = utf8_encode($line);
  197.                 }
  198.             }
  199.             
  200.             return $lines;
  201.         }
  202.     }
  203.  
  204.     protected function transformSourceFile($filename, $coverageInformation, $classStartLine = 1)
  205.     {
  206.         $sourceElement = $this->doc->createElement('sourcefile');
  207.         $sourceElement->setAttribute('name', basename($filename));
  208.         
  209.         /**
  210.          * Add original/full filename to document
  211.          */
  212.         $sourceElement->setAttribute('sourcefile', $filename);
  213.  
  214.         $filelines = $this->highlightSourceFile($filename);
  215.  
  216.         $linenr = 1;
  217.  
  218.         foreach ($filelines as $line)
  219.         {
  220.             $lineElement = $this->doc->createElement('sourceline');
  221.             $lineElement->setAttribute('coveredcount', (isset($coverageInformation[$linenr]) ? $coverageInformation[$linenr] : '0'));
  222.  
  223.             if ($linenr == $classStartLine)
  224.             {
  225.                 $lineElement->setAttribute('startclass', 1);
  226.             }
  227.  
  228.             $textnode = $this->doc->createTextNode($line);
  229.             $lineElement->appendChild($textnode);
  230.  
  231.             $sourceElement->appendChild($lineElement);
  232.  
  233.             $linenr++;
  234.         }
  235.  
  236.         return $sourceElement;
  237.     }
  238.     
  239.     protected function filterCovered($var)
  240.     {
  241.         return ($var >= 0);
  242.     }
  243.  
  244.     protected function transformCoverageInformation($filename, $coverageInformation)
  245.     {
  246.         // Strip last line of coverage information
  247.         end($coverageInformation);
  248.         unset($coverageInformation[key($coverageInformation)]);
  249.         
  250.         $classes = PHPUnitUtil::getDefinedClasses($filename, $this->classpath);
  251.         
  252.         if (is_array($classes))
  253.         {
  254.             foreach ($classes as $classname)
  255.             {
  256.                 $reflection = new ReflectionClass($classname);
  257.                 
  258.                 $methods = $reflection->getMethods();
  259.                 
  260.                 $classElement = $this->doc->createElement('class');
  261.                 $classElement->setAttribute('name', $reflection->getName());
  262.                 
  263.                 $this->addClassToPackage($reflection->getName(), $classElement);
  264.  
  265.                 $classStartLine = $reflection->getStartLine();
  266.                 
  267.                 $methodscovered = 0;
  268.                 $methodcount = 0;
  269.                 
  270.                 // Strange PHP5 reflection bug, classes without parent class or implemented interfaces seem to start one line off
  271.                 if ($reflection->getParentClass() == NULL && count($reflection->getInterfaces()) == 0)
  272.                 {
  273.                     unset($coverageInformation[$classStartLine + 1]);
  274.                 }
  275.                 else
  276.                 {
  277.                     unset($coverageInformation[$classStartLine]);
  278.                 }
  279.                 
  280.                 reset($coverageInformation);                
  281.                 
  282.                 foreach ($methods as $method)
  283.                 {
  284.                     // PHP5 reflection considers methods of a parent class to be part of a subclass, we don't
  285.                     if ($method->getDeclaringClass()->getName() != $reflection->getName())
  286.                     {
  287.                         continue;
  288.                     }
  289.  
  290.                     // small fix for XDEBUG_CC_UNUSED
  291.                     if (isset($coverageInformation[$method->getStartLine()]))
  292.                     {
  293.                         unset($coverageInformation[$method->getStartLine()]);
  294.                     }
  295.  
  296.                     if (isset($coverageInformation[$method->getEndLine()]))
  297.                     {
  298.                         unset($coverageInformation[$method->getEndLine()]);
  299.                     }
  300.  
  301.                     if ($method->isAbstract())
  302.                     {
  303.                         continue;
  304.                     }
  305.  
  306.                     $linenr = key($coverageInformation);
  307.  
  308.                     while ($linenr !== null && $linenr < $method->getStartLine())
  309.                     {
  310.                         next($coverageInformation);
  311.                         $linenr = key($coverageInformation);
  312.                     }
  313.  
  314.                     if (current($coverageInformation) > 0 && $method->getStartLine() <= $linenr && $linenr <= $method->getEndLine())
  315.                     {
  316.                         $methodscovered++;
  317.                     }
  318.  
  319.                     $methodcount++;
  320.                 }
  321.  
  322.                 $statementcount = count($coverageInformation);
  323.                 $statementscovered = count(array_filter($coverageInformation, array($this, 'filterCovered')));
  324.  
  325.                 $classElement->appendChild($this->transformSourceFile($filename, $coverageInformation, $classStartLine));
  326.  
  327.                 $classElement->setAttribute('methodcount', $methodcount);
  328.                 $classElement->setAttribute('methodscovered', $methodscovered);
  329.                 $classElement->setAttribute('statementcount', $statementcount);
  330.                 $classElement->setAttribute('statementscovered', $statementscovered);
  331.                 $classElement->setAttribute('totalcount', $methodcount + $statementcount);
  332.                 $classElement->setAttribute('totalcovered', $methodscovered + $statementscovered);
  333.             }
  334.         }
  335.     }
  336.  
  337.     protected function calculateStatistics()
  338.     {
  339.         $packages = $this->doc->documentElement->getElementsByTagName('package');
  340.  
  341.         $totalmethodcount = 0;
  342.         $totalmethodscovered = 0;
  343.  
  344.         $totalstatementcount = 0;
  345.         $totalstatementscovered = 0;
  346.  
  347.         foreach ($packages as $package)
  348.         {
  349.             $methodcount = 0;
  350.             $methodscovered = 0;
  351.  
  352.             $statementcount = 0;
  353.             $statementscovered = 0;
  354.  
  355.             $classes = $package->getElementsByTagName('class');
  356.  
  357.             foreach ($classes as $class)
  358.             {
  359.                 $methodcount += $class->getAttribute('methodcount');
  360.                 $methodscovered += $class->getAttribute('methodscovered');
  361.  
  362.                 $statementcount += $class->getAttribute('statementcount');
  363.                 $statementscovered += $class->getAttribute('statementscovered');
  364.             }
  365.  
  366.             $package->setAttribute('methodcount', $methodcount);
  367.             $package->setAttribute('methodscovered', $methodscovered);
  368.  
  369.             $package->setAttribute('statementcount', $statementcount);
  370.             $package->setAttribute('statementscovered', $statementscovered);
  371.  
  372.             $package->setAttribute('totalcount', $methodcount + $statementcount);
  373.             $package->setAttribute('totalcovered', $methodscovered + $statementscovered);
  374.  
  375.             $totalmethodcount += $methodcount;
  376.             $totalmethodscovered += $methodscovered;
  377.  
  378.             $totalstatementcount += $statementcount;
  379.             $totalstatementscovered += $statementscovered;
  380.         }
  381.  
  382.         $this->doc->documentElement->setAttribute('methodcount', $totalmethodcount);
  383.         $this->doc->documentElement->setAttribute('methodscovered', $totalmethodscovered);
  384.  
  385.         $this->doc->documentElement->setAttribute('statementcount', $totalstatementcount);
  386.         $this->doc->documentElement->setAttribute('statementscovered', $totalstatementscovered);
  387.  
  388.         $this->doc->documentElement->setAttribute('totalcount', $totalmethodcount + $totalstatementcount);
  389.         $this->doc->documentElement->setAttribute('totalcovered', $totalmethodscovered + $totalstatementscovered);
  390.     }
  391.  
  392.     function main()
  393.     {
  394.         $this->log("Transforming coverage report");
  395.         
  396.         $database = new PhingFile($this->project->getProperty('coverage.database'));
  397.         
  398.         $props = new Properties();
  399.         $props->load($database);
  400.  
  401.         foreach ($props->keys() as $filename)
  402.         {
  403.             $file = unserialize($props->getProperty($filename));
  404.  
  405.             $this->transformCoverageInformation($file['fullname'], $file['coverage']);
  406.         }
  407.         
  408.         $this->calculateStatistics();
  409.  
  410.         $this->doc->save($this->outfile);
  411.  
  412.         foreach ($this->transformers as $transformer)
  413.         {
  414.             $transformer->setXmlDocument($this->doc);
  415.             $transformer->transform();
  416.         }
  417.     }
  418. }
  419. ?>