NuSphere Forums Forum Index
NuSphere Forums
Reply to topic
php code coverage


Joined: 02 Aug 2007
Posts: 23
Reply with quote
It would be really useful for me if PhpEd included a code coverage tool for use with my unit tests. I use SimpleTest for unit tests, and, fortunately, there is no need for this to be integrated into PhpEd. However, code coverage would need to be implemented into PhpEd, since the only code coverage tools I know of utilize the Xdebug debugger instead of dbg, and I believe that you cannot have both of these installed at the same time. The profiler is the closest thing PhpEd has to a code coverage tool, and could easily be modified to be used as such. The main difference between the kind of report I would want to see with a code coverage tool and the report already displayed by the profiler, is that a code coverage report would need to show which lines were executed 0 times. You would also need to be able to specify which files it should report about. As far as I know there is no setting to make the profiler show which lines were not executed, but if there is, please enlighten me.
View user's profileFind all posts by INTPnerdSend private message
Site Admin

Joined: 13 Jul 2003
Posts: 8328
Reply with quote
I think it's possible with DBG.
First you'd need to add auto_append_file where you'll put the following lines in php:

Code:
<?php

$modules=array();
if (dbg_get_all_module_names($modules) >= 0) {
   $mod_num = $modules['mod_no'];
   $mod_name = $modules['mod_name'];
   foreach($mod_num as $idx=>$mod_no) {
      if ($mod_name[$idx] === __FILE__) continue;
      echo $mod_name[$idx] . "\n";
      $lines = array();
      dbg_get_all_source_lines($mod_no, $lines);
      $line_numbers = $lines['line_no'];
      var_dump($line_numbers);
      $profdata = array();
      dbg_get_profiler_results($profdata);
      $hit_count=$profdata['hit_count'];
      var_dump($hit_count);
      $hit_lines=$profdata['line_no'];
      var_dump($hit_lines);
   }
}

?>


$line_numbers contains all the lines with code in file $mod_name[$idx]
$hit_lines contains all the lines numbers that got at least one hit
$hit_count contains hit numbers for the lines in $hit_lines

To get the results all you need is to run profiler.

_________________
The PHP IDE team
View user's profileFind all posts by dmitriSend private messageVisit poster's website


Joined: 02 Aug 2007
Posts: 23
Reply with quote
Thank you! That provides me with all the data that I need to create custom reports! I wrote a function that only displays the lines that got executed 0 times, but for some reason it sometimes does not display all of the missed lines. Here is the code:

Code:
function EchoCodeCoverageReport($runModules = null)
   {
      $ranModules = null;
      $ranModulesIdx = 0;
      $modules = array();
      echo '<BR>';
      if (dbg_get_all_module_names($modules) >= 0)
      {
         $mod_num = $modules['mod_no'];
         $mod_name = $modules['mod_name'];
         echo 'Code Coverage Report:<BR>';
         
         foreach($mod_num as $idx => $mod_no)
         {
            if ($mod_name[$idx] === __FILE__)
            {
               continue;
            }
            if($runModules === null || in_array($mod_name[$idx], $runModules))
            {
               if($runModules !== null)
               {
                  $ranModules[$ranModulesIdx] = $mod_name[$idx];
                  $ranModulesIdx++;
               }
               echo "module name: {$mod_name[$idx]}<BR>";
               $lines = array();
               dbg_get_all_source_lines($mod_no, $lines);
               $line_numbers = $lines['line_no'];
               $profdata = array();
               dbg_get_profiler_results($profdata);
               $hit_lines = $profdata['line_no'];
               $missedLines = null;
               $i = 0;
               foreach($line_numbers as $value)
               {
                  if(!in_array($value, $hit_lines))
                  {
                     $missedLines[$i] = $value;
                     $i++;
                  }
               }
               $numMissedLines = 0;
               if($missedLines !== null)
               {
                  $missedLines =& array_unique($missedLines);
                  if(!sort($missedLines))
                  {
                     echo 'Error sorting $missedLines!<BR>';
                  }
                  $numMissedLines = count($missedLines);
               }
               if($numMissedLines > 0)
               {
                  echo "<font color = 'red'>";
               }
               else
               {
                  echo "<font color = 'green'>";
               }
               echo "Missed $numMissedLines lines";
               if($numMissedLines > 0)
               {
                  echo ':<BR>';
                  foreach($missedLines as $missed)
                  {
                     echo $missed."<BR>";
                  }
               }
               echo "</font><BR><BR><BR><BR><BR><BR><BR><BR>";
            }
         }
      }
      if($runModules !== null)
      {
         if($ranModules !== null)
         {
            foreach($runModules as $value)
            {
               if(!in_array($value, $ranModules))
               {
                  echo "<font color = 'red'>Did not run module: $value<BR></font>";
               }
            }
         }
         else
         {
            foreach($runModules as $value)
            {
               echo "Did not run module: $value<BR>";
            }
         }
      }
   }
You can optionally pass in an array of modules that you want it to report on exclusively. I would post an example where it does not show all the missed lines but I don't have one that I can post yet. Any ideas why it does not always show all the missed lines?
View user's profileFind all posts by INTPnerdSend private message


Joined: 02 Aug 2007
Posts: 23
Reply with quote
From the code I posted before, right after this:
Code:
               dbg_get_all_source_lines($mod_no, $lines);
               $line_numbers = $lines['line_no'];
               $profdata = array();
               dbg_get_profiler_results($profdata);
               $hit_lines = $profdata['line_no'];

Sometimes $hit_lines contains lines that were never hit. I will try running dmitri's code and see if the same problem occurs.
View user's profileFind all posts by INTPnerdSend private message


Joined: 02 Aug 2007
Posts: 23
Reply with quote
The same problem occurs with the code that dmitri posted. What is interesting is that the profiler tab that opens up seems to be accurate. It is not reporting the same missed lines as being hit. Are you sure that this is the correct way to get all the hit lines?
View user's profileFind all posts by INTPnerdSend private message


Joined: 02 Aug 2007
Posts: 23
Reply with quote
OK, here is a complete code example that will show you what I am talking about. This code is inside of a file named CodeCoverageTest.php
Code:
<?php
//class CodeCoverageTest
//{
   function ExecuteNonQuery($sql)
   {
      if(mysql_query($sql) === FALSE)
      {
         trigger_error("MySQL Error - SQL: \"$sql\"", E_USER_ERROR);
      }
   }
//}

require_once('CodeCoverage.php');
CodeCoverage::EchoReport(array("C:\\www\\wwwACS\\ACSmanager\\CodeCoverageTest.php"));
?>


Here is the code from the file called CodeCoverage.php:
Code:
<?php
class CodeCoverage
{
   function EchoReport($runModules = null)
   {
      $ranModules = array();
      $ranModulesIdx = 0;
      $modules = array();
      echo '<BR>';
      if (dbg_get_all_module_names($modules) >= 0)
      {
         $mod_num = $modules['mod_no'];
         $mod_name = $modules['mod_name'];
         echo 'Code Coverage Report:<BR>';
         
         foreach($mod_num as $idx => $mod_no)
         {
            if ($mod_name[$idx] === __FILE__)
            {
               continue;
            }
            if($runModules === null || in_array($mod_name[$idx], $runModules))
            {
               if($runModules)
               {
                  $ranModules[$ranModulesIdx] = $mod_name[$idx];
                  $ranModulesIdx++;
               }
               echo "module name: {$mod_name[$idx]}<BR>";
               $lines = array();
               dbg_get_all_source_lines($mod_no, $lines);
               $line_numbers = $lines['line_no'];
               $profdata = array();
               dbg_get_profiler_results($profdata);
               $hit_count=$profdata['hit_count'];
               $hit_lines = $profdata['line_no'];
               $missedLines = null;
               $i = 0;
               foreach($line_numbers as $value)
               {
                  if(!in_array($value, $hit_lines))
                  {
                     $missedLines[$i] = $value;
                     $i++;
                  }
               }
               $numMissedLines = 0;
               if($missedLines !== null)
               {
                  $missedLines =& array_unique($missedLines);
                  if(!sort($missedLines))
                  {
                     echo 'Error sorting $missedLines!<BR>';
                  }
                  $numMissedLines = count($missedLines);
               }
               if($numMissedLines > 0)
               {
                  echo "<font color = 'red'>";
               }
               else
               {
                  echo "<font color = 'green'>";
               }
               echo "Missed $numMissedLines lines";
               if($numMissedLines > 0)
               {
                  echo ':<BR>';
                  foreach($missedLines as $missed)
                  {
                     echo $missed."<BR>";
                  }
               }
               echo "</font><BR><BR><BR><BR><BR><BR><BR><BR>";
            }
         }
      }
      if($runModules)
      {
         if($ranModules)
         {
            foreach($runModules as $value)
            {
               if(!in_array($value, $ranModules))
               {
                  echo "<font color = 'red'>Did not run module: $value<BR></font>";
               }
            }
         }
         else
         {
            echo "<font color = 'red'>";
            foreach($runModules as $value)
            {
               echo "Did not run module: $value<BR>";
            }
            echo "</font>";
         }
      }
   }
}
/*
class CodeCoverage
{
   function EchoReport($runModules = null)
   {
      $modules=array();
      if (dbg_get_all_module_names($modules) >= 0)
      {
            $mod_num = $modules['mod_no'];
            $mod_name = $modules['mod_name'];
            foreach($mod_num as $idx=>$mod_no)
            {
               if ($mod_name[$idx] === __FILE__)
               {
                  continue;
            }
               echo "<BR>{$mod_name[$idx]}<BR>";
               $lines = array();
               dbg_get_all_source_lines($mod_no, $lines);
               $line_numbers = $lines['line_no'];
               //echo "<BR>line_numbers:<BR>";
               //var_dump($line_numbers);
               $profdata = array();
               dbg_get_profiler_results($profdata);
               $hit_count=$profdata['hit_count'];
               //echo "<BR>hit_count:<BR>";
               //var_dump($hit_count);
               $hit_lines=$profdata['line_no'];
               echo "<BR>hit_lines:<BR>";
               var_dump($hit_lines);
            }
      }
   }
}
*/
?>


Just run the file CodeCoverageTest.php with the profiler and go to the output tab. It shows 0 missed lines in CodeCoverageTest.php.
Go into the Tools->Settings and in the Debugger settings check the box that says Run profiler with debugger. Now put a break point in CodeCoverage.php on the line 37, the one that has $missedLines = null; Now run the debugger on CodeCoverageTest.php and after the break point is reached, examine the variables and you will notice that it shows lines 6 and 8 from CodeCoverageTest.php as being in the $hit_lines array and having a hit count of 1 in the $hit_count array. If you let it finish, and then look at the Profiler tab, it does not show lines 6 or 8 as having been hit. If you delete the commented out code from CodeCoverageTest.php, and run the profiler on it again, it will show line 4 as having been missed(the beginning of the if statement), but still not line 6(the trigger_error line). Please help me!
View user's profileFind all posts by INTPnerdSend private message


Joined: 02 Aug 2007
Posts: 23
Reply with quote
What do you think? Is there a bug in my code or in the functions that I am calling? Or maybe the problem is specific to my environment? Can someone else try out the example I posted above and tell me if they have this problem too?
View user's profileFind all posts by INTPnerdSend private message
Site Admin

Joined: 13 Jul 2003
Posts: 8328
Reply with quote
Quote:
Just run the file CodeCoverageTest.php with the profiler and go to the output tab. It shows 0 missed lines in CodeCoverageTest.php

your code isn't correct.
Indeed. Take a look at the $profdata.
In its 'mod_no' array, you'll see that only idices 0, 1 and 2 relate to mod_no=1 (the file you inspect) while all the other relate to mod_no=2 which is CodeCoverage.php.
Therefore you can take only 0, 1 and 2 indices from 'line_no' and 'hit_count' (if needed) subarrays.
If you do this, you'll see that only lines 4, 13 and 14 were hit.

_________________
The PHP IDE team
View user's profileFind all posts by dmitriSend private messageVisit poster's website


Joined: 02 Aug 2007
Posts: 23
Reply with quote
Thank you dmitri! There was no reason for me to believe that dbg_get_profiler_results() would only return the data for one module at at time, since it took no module specifying parameters. I guess since it was in a foreach loop I got stuck in that mindset. Here is the working version of the EchoReport() function in case anyone cares:
Code:
function EchoReport($reportModuleNames = null)
{
   $ranModuleNames = array();
   $ranModulesIdx = 0;
   $ranModulesInfo = array();
   echo '<BR>';
   if(dbg_get_all_module_names($ranModulesInfo) >= 0)
   {
      $moduleNumArray = $ranModulesInfo['mod_no'];
      $moduleNameArray = $ranModulesInfo['mod_name'];
      echo 'Code Coverage Report:<BR><BR>';
      $profilerData = array();
      dbg_get_profiler_results($profilerData);
      $hitLines = $profilerData['line_no'];
      foreach($moduleNumArray as $idx => $moduleNumber)
      {
         if($moduleNameArray[$idx] === __FILE__)
         {
            continue;
         }
         if($reportModuleNames === null || in_array($moduleNameArray[$idx], $reportModuleNames))
         {
            if($reportModuleNames)
            {
               $ranModuleNames[$ranModulesIdx] = $moduleNameArray[$idx];
               $ranModulesIdx++;
            }
            echo "module name: {$moduleNameArray[$idx]}<BR>";
            $lines = array();
            dbg_get_all_source_lines($moduleNumber, $lines);
            $lineNumbersInModule = $lines['line_no'];
            $missedLineNumsInModule = null;
            $missedLinesIdx = 0;
            foreach($lineNumbersInModule as $lineNumber)
            {
               $keys = array_keys($hitLines, $lineNumber);
               $lineNumberWasHit = false;
               if($keys)
               {
                  foreach($keys as $key)
                  {
                     if($profilerData['mod_no'][$key] == $moduleNumber)
                     {
                        $lineNumberWasHit = true;
                        break;
                     }
                  }
               }
               if(!$lineNumberWasHit)
               {
                  $missedLineNumsInModule[$missedLinesIdx] = $lineNumber;
                  $missedLinesIdx++;
               }
            }
            $numMissedLines = 0;
            if($missedLineNumsInModule !== null)
            {
               $missedLineNumsInModule =& array_unique($missedLineNumsInModule);
               if(!sort($missedLineNumsInModule))
               {
                  echo 'Error sorting $missedLineNumsInModule!<BR>';
               }
               $numMissedLines = count($missedLineNumsInModule);
            }
            if($numMissedLines > 0)
            {
               echo "<font color = 'red'>";
            }
            else
            {
               echo "<font color = 'green'>";
            }
            echo "Missed $numMissedLines lines";
            if($numMissedLines > 0)
            {
               echo ':<BR>';
               foreach($missedLineNumsInModule as $lineNumber)
               {
                  echo $lineNumber."<BR>";
               }
            }
            echo "</font><BR><BR><BR><BR>";
         }
      }
   }
   if($reportModuleNames)
   {
      if($ranModuleNames)
      {
         foreach($reportModuleNames as $value)
         {
            if(!in_array($value, $ranModuleNames))
            {
               echo "<font color = 'red'>Did not run module: $value<BR></font>";
            }
         }
      }
      else
      {
         echo "<font color = 'red'>";
         foreach($reportModuleNames as $value)
         {
            echo "Did not run module: $value<BR>";
         }
         echo "</font>";
      }
   }
}
View user's profileFind all posts by INTPnerdSend private message


Joined: 02 Aug 2007
Posts: 23
Reply with quote
There was still a major bug in the code from that last post. Hopefully I got all the bugs out now.
View user's profileFind all posts by INTPnerdSend private message
Site Admin

Joined: 13 Jul 2003
Posts: 8328
Reply with quote
thanks, I updated your post with new code.

_________________
The PHP IDE team
View user's profileFind all posts by dmitriSend private messageVisit poster's website


Joined: 07 Jan 2015
Posts: 1
Reply with quote
I believe that you cannot have both of these installed at the same time. The profiler is the closest thing PhpEd has to a code coverage tool, and could easily be modified to be used as such. The main difference between the kind of report I would want to see with a code coverage tool and the report already displayed by the profiler, is that a code coverage report would need to show which lines were executed 0 times. You would also need to be able to specify which files it should report about. As far as I know there is no setting to make the profiler show which lines were not executed



________________
We are the pioneers in providing toefl and exams with exam pass Download our latest passguide gmat and Florida Memorial University ccna or pass real exam of
View user's profileFind all posts by OvereatingSend private message
php code coverage
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum
All times are GMT - 5 Hours  
Page 1 of 1  

  
  
 Reply to topic