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.