Author: bschussek
Date: 2010-04-09 14:29:02 +0200 (Fri, 09 Apr 2010)
New Revision: 29053

Modified:
   tools/lime/trunk/lib/lime.php
Log:
[lime] Backported error and exception handling from Lime 2. lime now displays 
warnings, notices and catchable exceptions (=exceptions thrown after the 
lime_test object is created) both when running single tests and when running 
test suites.

Modified: tools/lime/trunk/lib/lime.php
===================================================================
--- tools/lime/trunk/lib/lime.php       2010-04-09 12:19:39 UTC (rev 29052)
+++ tools/lime/trunk/lib/lime.php       2010-04-09 12:29:02 UTC (rev 29053)
@@ -35,9 +35,10 @@
     }
 
     $this->options = array_merge(array(
-      'force_colors' => false,
-      'output'       => null,
-      'verbose'      => false,
+      'force_colors'    => false,
+      'output'          => null,
+      'verbose'         => false,
+      'error_reporting' => true,
     ), $options);
 
     $this->output = $this->options['output'] ? $this->options['output'] : new 
lime_output($this->options['force_colors']);
@@ -46,12 +47,15 @@
     self::$all_results[] = array(
       'file'  => $caller[0],
       'tests' => array(),
-      'stats' => array('plan' => $plan, 'total' => 0, 'failed' => array(), 
'passed' => array(), 'skipped' => array()),
+      'stats' => array('plan' => $plan, 'total' => 0, 'failed' => array(), 
'passed' => array(), 'skipped' => array(), 'errors' => array()),
     );
 
     $this->results = &self::$all_results[count(self::$all_results) - 1];
 
     null !== $plan and $this->output->echoln(sprintf("1..%d", $plan));
+
+    set_error_handler(array($this, 'handle_error'));
+    set_exception_handler(array($this, 'handle_exception'));
   }
 
   static public function reset()
@@ -87,12 +91,13 @@
       $testsuite->setAttribute('name', basename($result['file'], '.php'));
       $testsuite->setAttribute('file', $result['file']);
       $testsuite->setAttribute('failures', count($result['stats']['failed']));
-      $testsuite->setAttribute('errors', 0);
+      $testsuite->setAttribute('errors', count($result['stats']['errors']));
       $testsuite->setAttribute('skipped', count($result['stats']['skipped']));
       $testsuite->setAttribute('tests', $result['stats']['plan']);
       $testsuite->setAttribute('assertions', $result['stats']['plan']);
 
       $failures += count($result['stats']['failed']);
+      $errors += count($result['stats']['errors']);
       $skipped += count($result['stats']['skipped']);
       $assertions += $result['stats']['plan'];
 
@@ -107,7 +112,7 @@
         {
           $testcase->appendChild($failure = $dom->createElement('failure'));
           $failure->setAttribute('type', 'lime');
-          if ($test['error'])
+          if (isset($test['error']))
           {
             $failure->appendChild($dom->createTextNode($test['error']));
           }
@@ -495,9 +500,15 @@
     $this->output->info($message);
   }
 
-  public function error($message)
+  public function error($message, $file = null, $line = null, array $traces = 
array())
   {
-    $this->output->error($message);
+    $this->output->error($message, $file, $line, $traces);
+
+       $this->results['stats']['errors'][] = array(
+         'message' => $message,
+         'file' => $file,
+         'line' => $line,
+       );
   }
 
   protected function update_stats()
@@ -531,15 +542,48 @@
     $last = count($traces) - 1;
     return array($traces[$last]['file'], $traces[$last]['line']);
   }
+
+  public function handle_error($code, $message, $file, $line, $context)
+  {
+    if (!$this->options['error_reporting'] || ($code & error_reporting()) == 0)
+    {
+      return false;
+    }
+
+    switch ($code)
+    {
+      case E_WARNING:
+        $type = 'Warning';
+        break;
+      default:
+        $type = 'Notice';
+        break;
+    }
+
+    $trace = debug_backtrace();
+    array_shift($trace); // remove the handle_error() call from the trace
+
+    $this->error($type.': '.$message, $file, $line, $trace);
+  }
+
+  public function handle_exception(Exception $exception)
+  {
+    $this->error(get_class($exception).': '.$exception->getMessage(), 
$exception->getFile(), $exception->getLine(), $exception->getTrace());
+
+    // exception was handled
+    return true;
+  }
 }
 
 class lime_output
 {
   public $colorizer = null;
+  public $base_dir = null;
 
-  public function __construct($force_colors = false)
+  public function __construct($force_colors = false, $base_dir = null)
   {
     $this->colorizer = new lime_colorizer($force_colors);
+    $this->base_dir = $base_dir === null ? getcwd() : $base_dir;
   }
 
   public function diag()
@@ -561,16 +605,82 @@
     echo $this->colorizer->colorize(sprintf('> %s', $message), 
'INFO_BAR')."\n";
   }
 
-  public function error($message)
+  public function error($message, $file = null, $line = null, $traces = 
array())
   {
-    echo $this->colorizer->colorize(sprintf(' %s ', $message), 'RED_BAR')."\n";
+    if ($file !== null)
+    {
+      $message .= sprintf("\n(in %s on line %s)", $file, $line);
+    }
+
+       // some error messages contain absolute file paths
+    $message = $this->strip_base_dir($message);
+
+    $space = $this->colorizer->colorize(str_repeat(' ', 71), 'RED_BAR')."\n";
+    $message = trim($message);
+    $message = wordwrap($message, 66, "\n");
+
+    echo "\n".$space;
+    foreach (explode("\n", $message) as $message_line)
+    {
+      echo $this->colorizer->colorize(str_pad('  '.$message_line, 71, ' '), 
'RED_BAR')."\n";
+    }
+    echo $space."\n";
+
+    if (count($traces) > 0)
+    {
+      echo $this->colorizer->colorize('Exception trace:', 'COMMENT')."\n";
+
+      $this->print_trace(null, $file, $line);
+
+      foreach ($traces as $trace)
+      {
+        if (array_key_exists('class', $trace))
+        {
+          $method = sprintf('%s%s%s()', $trace['class'], $trace['type'], 
$trace['function']);
+        }
+        else
+        {
+          $method = sprintf('%s()', $trace['function']);
+        }
+
+        if (array_key_exists('file', $trace))
+        {
+          $this->print_trace($method, $trace['file'], $trace['line']);
+        }
+        else
+        {
+          $this->print_trace($method);
+        }
+      }
+
+      echo "\n";
+    }
   }
 
+  protected function print_trace($method = null, $file = null, $line = null)
+  {
+    if (!is_null($method))
+    {
+      $method .= ' ';
+    }
+
+    echo '  '.$method.'at ';
+
+    if (!is_null($file) && !is_null($line))
+    {
+      printf("%s:%s\n", 
$this->colorizer->colorize($this->strip_base_dir($file), 'TRACE'), 
$this->colorizer->colorize($line, 'TRACE'));
+    }
+    else
+    {
+      echo "[internal function]\n";
+    }
+  }
+
   public function echoln($message, $colorizer_parameter = null, $colorize = 
true)
   {
     if ($colorize)
     {
-      $message = preg_replace('/(?:^|\.)((?:not ok|dubious) *\d*)\b/e', 
'$this->colorizer->colorize(\'$1\', \'ERROR\')', $message);
+      $message = preg_replace('/(?:^|\.)((?:not ok|dubious|errors) *\d*)\b/e', 
'$this->colorizer->colorize(\'$1\', \'ERROR\')', $message);
       $message = preg_replace('/(?:^|\.)(ok *\d*)\b/e', 
'$this->colorizer->colorize(\'$1\', \'INFO\')', $message);
       $message = preg_replace('/"(.+?)"/e', 
'$this->colorizer->colorize(\'$1\', \'PARAMETER\')', $message);
       $message = preg_replace('/(\->|\:\:)?([a-zA-Z0-9_]+?)\(\)/e', 
'$this->colorizer->colorize(\'$1$2()\', \'PARAMETER\')', $message);
@@ -588,6 +698,11 @@
   {
     echo $this->colorizer->colorize($message.str_repeat(' ', 71 - min(71, 
strlen($message))), 'RED_BAR')."\n";
   }
+
+  protected function strip_base_dir($text)
+  {
+    return str_replace(DIRECTORY_SEPARATOR, '/', 
str_replace(realpath($this->base_dir).DIRECTORY_SEPARATOR, '', $text));
+  }
 }
 
 class lime_output_color extends lime_output
@@ -653,6 +768,7 @@
 
 lime_colorizer::style('ERROR', array('bg' => 'red', 'fg' => 'white', 'bold' => 
true));
 lime_colorizer::style('INFO', array('fg' => 'green', 'bold' => true));
+lime_colorizer::style('TRACE', array('fg' => 'green', 'bold' => true));
 lime_colorizer::style('PARAMETER', array('fg' => 'cyan'));
 lime_colorizer::style('COMMENT', array('fg' => 'yellow'));
 
@@ -768,14 +884,18 @@
       $this->stats['files'][$file] = array();
       $stats = &$this->stats['files'][$file];
 
-      $relative_file = $this->get_relative_file($file);
+      $relative_file = $this->strip_base_dir($file);
 
       $test_file = tempnam(sys_get_temp_dir(), 'lime');
       $result_file = tempnam(sys_get_temp_dir(), 'lime');
       file_put_contents($test_file, <<<EOF
 <?php
+function lime_shutdown()
+{
+  file_put_contents('$result_file', serialize(lime_test::to_array()));
+}
+register_shutdown_function('lime_shutdown');
 include('$file');
-file_put_contents('$result_file', serialize(lime_test::to_array()));
 EOF
       );
 
@@ -789,7 +909,7 @@
       $stats['output'] = $output ? unserialize($output) : '';
       if (!$stats['output'])
       {
-        $stats['output'] = array(array('file' => $file, 'tests' => array(), 
'stats' => array('plan' => 1, 'total' => 1, 'failed' => array(0), 'passed' => 
array(), 'skipped' => array())));
+        $stats['output'] = array(array('file' => $file, 'tests' => array(), 
'stats' => array('plan' => 1, 'total' => 1, 'failed' => array(0), 'passed' => 
array(), 'skipped' => array(), 'errors' => array())));
       }
       unlink($result_file);
 
@@ -798,7 +918,7 @@
       $delta = 0;
       if ($return > 0)
       {
-        $stats['status'] = 'dubious';
+        $stats['status'] = $file_stats['errors'] ? 'errors' : 'dubious';
         $stats['status_code'] = $return;
       }
       else
@@ -813,19 +933,19 @@
         $delta = $file_stats['plan'] - $file_stats['total'];
         if (0 != $delta)
         {
-          $stats['status'] = 'dubious';
+          $stats['status'] = $file_stats['errors'] ? 'errors' : 'dubious';
           $stats['status_code'] = 255;
         }
         else
         {
-          $stats['status'] = $file_stats['failed'] ? 'not ok' : 'ok';
+          $stats['status'] = $file_stats['failed'] ? 'not ok' : 
($file_stats['errors'] ? 'errors' : 'ok');
           $stats['status_code'] = 0;
         }
       }
 
       $this->output->echoln(sprintf('%s%s%s', substr($relative_file, -min(67, 
strlen($relative_file))), str_repeat('.', 70 - min(67, 
strlen($relative_file))), $stats['status']));
 
-      if (0 != $stats['status_code'])
+      if ('dubious' == $stats['status'])
       {
         $this->output->echoln(sprintf('    Test returned status %s', 
$stats['status_code']));
       }
@@ -853,24 +973,39 @@
 
         $this->output->echoln(sprintf("    Failed tests: %s", implode(', ', 
$file_stats['failed'])));
       }
+
+      if (false !== $file_stats && $file_stats['errors'])
+      {
+        $this->output->echoln('    Errors:');
+
+        $error_count = count($file_stats['errors']);
+        for ($i = 0; $i < 3 && $i < $error_count; ++$i)
+        {
+          $this->output->echoln('    - ' . 
$file_stats['errors'][$i]['message'], null, false);
+        }
+        if ($error_count > 3)
+        {
+          $this->output->echoln(sprintf('    ... and %s more', 
$error_count-3));
+        }
+      }
     }
 
     if (count($this->stats['failed_files']))
     {
-      $format = "%-30s  %4s  %5s  %5s  %s";
-      $this->output->echoln(sprintf($format, 'Failed Test', 'Stat', 'Total', 
'Fail', 'List of Failed'));
-      
$this->output->echoln("------------------------------------------------------------------");
+      $format = "%-30s  %4s  %5s  %5s  %5s  %s";
+      $this->output->echoln(sprintf($format, 'Failed Test', 'Stat', 'Total', 
'Fail', 'Errors', 'List of Failed'));
+      
$this->output->echoln("--------------------------------------------------------------------------");
       foreach ($this->stats['files'] as $file => $stat)
       {
         if (!in_array($file, $this->stats['failed_files']))
         {
           continue;
         }
-        $relative_file = $this->get_relative_file($file);
+        $relative_file = $this->strip_base_dir($file);
 
         if (isset($stat['output'][0]))
         {
-          $this->output->echoln(sprintf($format, substr($relative_file, 
-min(30, strlen($relative_file))), $stat['status_code'], 
count($stat['output'][0]['stats']['failed']) + 
count($stat['output'][0]['stats']['passed']), 
count($stat['output'][0]['stats']['failed']), implode(' ', 
$stat['output'][0]['stats']['failed'])));
+          $this->output->echoln(sprintf($format, substr($relative_file, 
-min(30, strlen($relative_file))), $stat['status_code'], 
count($stat['output'][0]['stats']['failed']) + 
count($stat['output'][0]['stats']['passed']), 
count($stat['output'][0]['stats']['failed']), 
count($stat['output'][0]['stats']['errors']), implode(' ', 
$stat['output'][0]['stats']['failed'])));
         }
         else
         {
@@ -902,11 +1037,11 @@
             if ($first)
             {
               $this->output->echoln('');
-              
$this->output->error($this->get_relative_file($testsuite['file']).$this->extension);
+              
$this->output->error($this->strip_base_dir($testsuite['file']).$this->extension);
               $first = false;
             }
 
-            $this->output->comment(sprintf('  at %s line %s', 
$this->get_relative_file($testsuite['tests'][$testcase]['file']).$this->extension,
 $testsuite['tests'][$testcase]['line']));
+            $this->output->comment(sprintf('  at %s line %s', 
$this->strip_base_dir($testsuite['tests'][$testcase]['file']).$this->extension, 
$testsuite['tests'][$testcase]['line']));
             $this->output->info('  
'.$testsuite['tests'][$testcase]['message']);
             $this->output->echoln($testsuite['tests'][$testcase]['error'], 
null, false);
           }
@@ -1080,7 +1215,7 @@
       $total_php_lines += $total_lines;
       $total_covered_lines += count($covered_lines);
 
-      $relative_file = $this->get_relative_file($file);
+      $relative_file = $this->strip_base_dir($file);
       $output->echoln(sprintf("%-70s %3.0f%%", substr($relative_file, -min(70, 
strlen($relative_file))), $percent), $percent == 100 ? 'INFO' : ($percent > 90 
? 'PARAMETER' : ($percent < 20 ? 'ERROR' : '')));
       if ($this->verbose && $is_covered && $percent != 100)
       {
@@ -1354,7 +1489,7 @@
     $this->files = array_merge($this->files, $files);
   }
 
-  protected function get_relative_file($file)
+  protected function strip_base_dir($file)
   {
     return str_replace(DIRECTORY_SEPARATOR, '/', 
str_replace(array(realpath($this->base_dir).DIRECTORY_SEPARATOR, 
$this->extension), '', $file));
   }

-- 
You received this message because you are subscribed to the Google Groups 
"symfony SVN" group.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to 
[email protected].
For more options, visit this group at 
http://groups.google.com/group/symfony-svn?hl=en.

Reply via email to