mirror of
https://github.com/php/php-src.git
synced 2026-03-31 12:42:29 +02:00
483 lines
11 KiB
PHP
483 lines
11 KiB
PHP
<?php
|
|
namespace phpdbg\testing {
|
|
|
|
/**
|
|
* TestConfigurationExceptions are thrown
|
|
* when the configuration prohibits tests executing
|
|
*
|
|
* @package phpdbg
|
|
* @subpackage testing
|
|
*/
|
|
class TestConfigurationException extends \Exception {
|
|
|
|
/**
|
|
*
|
|
* @param array Tests confguration
|
|
* @param message Exception message
|
|
* @param ... formatting parameters
|
|
*/
|
|
public function __construct() {
|
|
$argv = func_get_args();
|
|
|
|
if (count($argv)) {
|
|
|
|
$this->config = array_shift($argv);
|
|
$this->message = vsprintf(
|
|
array_shift($argv), $argv);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Tests is the console programming API for the test suite
|
|
*
|
|
* @package phpdbg
|
|
* @subpackage testing
|
|
*/
|
|
class Tests {
|
|
|
|
/**
|
|
* Construct the console object
|
|
*
|
|
* @param array basic configuration
|
|
* @param array command line
|
|
*/
|
|
public function __construct(&$config, $cmd) {
|
|
while (($key = array_shift($cmd))) {
|
|
switch (substr($key, 0, 1)) {
|
|
case '-': switch(substr($key, 1, 1)) {
|
|
case '-': {
|
|
$arg = substr($key, 2);
|
|
if (($e=strpos($arg, '=')) !== false) {
|
|
$key = substr($arg, 0, $e);
|
|
$value = substr($arg, $e+1);
|
|
} else {
|
|
$key = $arg;
|
|
$value = array_shift($cmd);
|
|
}
|
|
|
|
if (isset($key) && isset($value)) {
|
|
switch ($key) {
|
|
case 'phpdbg':
|
|
case 'width':
|
|
$config[$key] = $value;
|
|
break;
|
|
|
|
default: {
|
|
if (isset($config[$key])) {
|
|
if (is_array($config[$key])) {
|
|
$config[$key][] = $value;
|
|
} else {
|
|
$config[$key] = array($config[$key], $value);
|
|
}
|
|
} else {
|
|
$config[$key] = $value;
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
} break;
|
|
|
|
default:
|
|
$config['flags'][] = substr($key, 1);
|
|
} break;
|
|
}
|
|
}
|
|
|
|
if (!is_executable($config['phpdbg'])) {
|
|
throw new TestConfigurationException(
|
|
$config, 'phpdbg could not be found at the specified path (%s)', $config['phpdbg']);
|
|
} else $config['phpdbg'] = realpath($config['phpdbg']);
|
|
|
|
$conifg['width'] = (integer) $config['width'];
|
|
|
|
/* display properly, all the time */
|
|
if ($config['width'] < 50) {
|
|
$config['width'] = 50;
|
|
}
|
|
|
|
/* calculate column widths */
|
|
$config['lwidth'] = ceil($config['width'] / 3);
|
|
$config['rwidth'] = ceil($config['width'] - $config['lwidth']) - 5;
|
|
|
|
$this->config = &$config;
|
|
|
|
if (in_array('help', $this->config['flags'])||
|
|
in_array('h', $this->config['flags'])) {
|
|
$this->logUsage();
|
|
exit;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Find valid paths as specified by configuration
|
|
*
|
|
*/
|
|
public function findPaths($in = null) {
|
|
$paths = array();
|
|
$where = ($in != null) ? array($in) : $this->config['path'];
|
|
|
|
foreach ($where as &$path) {
|
|
if ($path) {
|
|
if (is_dir($path)) {
|
|
$paths[] = $path;
|
|
foreach (scandir($path) as $child) {
|
|
if ($child != '.' && $child != '..') {
|
|
$paths = array_merge(
|
|
$paths, $this->findPaths("$path/$child"));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return $paths;
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param string the path to log
|
|
*/
|
|
public function logPath($path) {
|
|
printf(
|
|
'%s [%s]%s',
|
|
str_repeat(
|
|
'-', $this->config['width'] - strlen($path)),
|
|
$path, PHP_EOL);
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param string the path to log
|
|
*/
|
|
public function logPathStats($path) {
|
|
if (!isset($this->stats[$path])) {
|
|
return;
|
|
}
|
|
|
|
$total = array_sum($this->stats[$path]);
|
|
|
|
if ($total) {
|
|
@$this->totals[true] += $this->stats[$path][true];
|
|
@$this->totals[false] += $this->stats[$path][false];
|
|
|
|
$stats = @sprintf(
|
|
"%d/%d %%%d",
|
|
$this->stats[$path][true],
|
|
$this->stats[$path][false],
|
|
(100 / $total) * $this->stats[$path][true]);
|
|
|
|
printf(
|
|
'%s [%s]%s',
|
|
str_repeat(
|
|
' ', $this->config['width'] - strlen($stats)),
|
|
$stats, PHP_EOL);
|
|
|
|
printf("%s%s", str_repeat('-', $this->config['width']+3), PHP_EOL);
|
|
printf("%s", PHP_EOL);
|
|
}
|
|
}
|
|
|
|
/**
|
|
*
|
|
*/
|
|
public function logStats() {
|
|
$total = array_sum($this->totals);
|
|
$stats = @sprintf(
|
|
"%d/%d %%%d",
|
|
$this->totals[true],
|
|
$this->totals[false],
|
|
(100 / $total) * $this->totals[true]);
|
|
printf(
|
|
'%s [%s]%s',
|
|
str_repeat(
|
|
' ', $this->config['width'] - strlen($stats)),
|
|
$stats, PHP_EOL);
|
|
|
|
}
|
|
|
|
/**
|
|
*
|
|
*/
|
|
protected function logUsage() {
|
|
printf('usage: php %s [flags] [options]%s', $this->config['exec'], PHP_EOL);
|
|
printf('[options]:%s', PHP_EOL);
|
|
printf("\t--path\t\tadd a path to scan outside of tests directory%s", PHP_EOL);
|
|
printf("\t--width\t\tset line width%s", PHP_EOL);
|
|
printf("\t--options\toptions to pass to phpdbg%s", PHP_EOL);
|
|
printf("\t--phpdbg\tpath to phpdbg binary%s", PHP_EOL);
|
|
printf('[flags]:%s', PHP_EOL);
|
|
printf("\t-nodiff\t\tdo not write diffs on failure%s", PHP_EOL);
|
|
printf("\t-nolog\t\tdo not write logs on failure%s", PHP_EOL);
|
|
printf('[examples]:%s', PHP_EOL);
|
|
printf("\tphp %s --phpdbg=/usr/local/bin/phpdbg --path=/usr/src/phpdbg/tests --options -n%s",
|
|
$this->config['exec'], PHP_EOL);
|
|
|
|
}
|
|
|
|
/**
|
|
* Find valid tests at the specified path (assumed valid)
|
|
*
|
|
* @param string a valid path
|
|
*/
|
|
public function findTests($path) {
|
|
$tests = array();
|
|
|
|
foreach (scandir($path) as $file) {
|
|
if ($file == '.' || $file == '..')
|
|
continue;
|
|
|
|
$test = sprintf('%s/%s', $path, $file);
|
|
|
|
if (preg_match('~\.test$~', $test)) {
|
|
yield new Test($this->config, $test);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param Test the test to log
|
|
*/
|
|
public function logTest($path, Test $test) {
|
|
@$this->stats[$path][($result=$test->getResult())]++;
|
|
|
|
printf(
|
|
"%-{$this->config['lwidth']}s %-{$this->config['rwidth']}s [%s]%s",
|
|
$test->name,
|
|
$test->purpose,
|
|
$result ? "PASS" : "FAIL",
|
|
PHP_EOL);
|
|
}
|
|
|
|
protected $config;
|
|
}
|
|
|
|
class Test {
|
|
/*
|
|
* Expect exact line for line match
|
|
*/
|
|
const EXACT = 0x00000001;
|
|
|
|
/*
|
|
* Expect strpos() !== false
|
|
*/
|
|
const STRING = 0x00000010;
|
|
|
|
/*
|
|
* Expect stripos() !== false
|
|
*/
|
|
const CISTRING = 0x00000100;
|
|
|
|
/**
|
|
* Constructs a new Test object given a specilized phpdbginit file
|
|
*
|
|
* @param array configuration
|
|
* @param string file
|
|
*/
|
|
public function __construct(&$config, &$file) {
|
|
if (($handle = fopen($file, 'r'))) {
|
|
while (($line = fgets($handle))) {
|
|
$trim = trim($line);
|
|
|
|
switch (substr($trim, 0, 1)) {
|
|
case '#': if (($chunks = array_map('trim', preg_split('~:~', substr($trim, 1), 2)))) {
|
|
if (property_exists($this, $chunks[0])) {
|
|
switch ($chunks[0]) {
|
|
case 'expect': {
|
|
if ($chunks[1]) {
|
|
switch (strtoupper($chunks[1])) {
|
|
case 'TEST::EXACT':
|
|
case 'EXACT': { $this->expect = TEST::EXACT; } break;
|
|
|
|
case 'TEST::STRING':
|
|
case 'STRING': { $this->expect = TEST::STRING; } break;
|
|
|
|
case 'TEST::CISTRING':
|
|
case 'CISTRING': { $this->expect = TEST::CISTRING; } break;
|
|
|
|
default:
|
|
throw new TestConfigurationException(
|
|
$this->config, "unknown type of expectation (%s)", $chunks[1]);
|
|
}
|
|
}
|
|
} break;
|
|
|
|
default: {
|
|
$this->$chunks[0] = $chunks[1];
|
|
}
|
|
}
|
|
} else switch(substr($trim, 1, 1)) {
|
|
case '#': { /* do nothing */ } break;
|
|
|
|
default: {
|
|
$this->match[] = ltrim(substr($trim, 1));
|
|
}
|
|
}
|
|
} break;
|
|
|
|
default:
|
|
break 2;
|
|
}
|
|
}
|
|
fclose($handle);
|
|
|
|
$this->config = &$config;
|
|
$this->file = &$file;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Obvious !!
|
|
*
|
|
*/
|
|
public function getResult() {
|
|
$options = sprintf(
|
|
'-i%s -qb', $this->file);
|
|
|
|
if ($this->options) {
|
|
$options = sprintf(
|
|
'%s %s %s',
|
|
$options,
|
|
$this->config['options'],
|
|
$this->options
|
|
);
|
|
} else {
|
|
$options = sprintf(
|
|
'%s %s', $options, $this->config['options']
|
|
);
|
|
}
|
|
|
|
$result = `{$this->config['phpdbg']} {$options}`;
|
|
|
|
if ($result) {
|
|
foreach (preg_split('~(\r|\n)~', $result) as $num => $line) {
|
|
if (!$line && !isset($this->match[$num]))
|
|
continue;
|
|
|
|
switch ($this->expect) {
|
|
case TEST::EXACT: {
|
|
if (strcmp($line, $this->match[$num]) !== 0) {
|
|
$this->diff['wants'][$num] = &$this->match[$num];
|
|
$this->diff['gets'][$num] = $line;
|
|
}
|
|
} continue 2;
|
|
|
|
case TEST::STRING: {
|
|
if (strpos($line, $this->match[$num]) === false) {
|
|
$this->diff['wants'][$num] = &$this->match[$num];
|
|
$this->diff['gets'][$num] = $line;
|
|
}
|
|
} continue 2;
|
|
|
|
case TEST::CISTRING: {
|
|
if (stripos($line, $this->match[$num]) === false) {
|
|
$this->diff['wants'][$num] = &$this->match[$num];
|
|
$this->diff['gets'][$num] = $line;
|
|
}
|
|
} continue 2;
|
|
}
|
|
}
|
|
}
|
|
|
|
$this->writeLog($result);
|
|
$this->writeDiff();
|
|
|
|
return (count($this->diff) == 0);
|
|
}
|
|
|
|
/**
|
|
* Write diff to disk if configuration allows it
|
|
*
|
|
*/
|
|
protected function writeDiff() {
|
|
$diff = sprintf(
|
|
'%s/%s.diff',
|
|
dirname($this->file), basename($this->file));
|
|
|
|
if (count($this->diff['wants'])) {
|
|
if (!in_array('nodiff', $this->config['flags'])) {
|
|
if (($diff = fopen($diff, 'w+'))) {
|
|
|
|
foreach ($this->diff['wants'] as $line => $want) {
|
|
$got = $this->diff['gets'][$line];
|
|
|
|
fprintf(
|
|
$diff, '(%d) -%s%s', $line+1, $want, PHP_EOL);
|
|
fprintf(
|
|
$diff, '(%d) +%s%s', $line+1, $got, PHP_EOL);
|
|
}
|
|
|
|
fclose($diff);
|
|
}
|
|
}
|
|
} else unlink($diff);
|
|
}
|
|
|
|
/**
|
|
* Write log to disk if configuration allows it
|
|
*
|
|
*/
|
|
protected function writeLog(&$result = null) {
|
|
$log = sprintf(
|
|
'%s/%s.log',
|
|
dirname($this->file), basename($this->file));
|
|
|
|
if (count($this->diff) && $result) {
|
|
if (!in_array('nolog', $this->config['flags'])) {
|
|
@file_put_contents(
|
|
$log, $result);
|
|
}
|
|
} else unlink($log);
|
|
}
|
|
|
|
public $name;
|
|
public $purpose;
|
|
public $file;
|
|
public $options;
|
|
public $expect;
|
|
public $match;
|
|
|
|
protected $diff;
|
|
protected $stats;
|
|
protected $totals;
|
|
}
|
|
}
|
|
|
|
namespace {
|
|
use \phpdbg\Testing\Test;
|
|
use \phpdbg\Testing\Tests;
|
|
|
|
$cwd = dirname(__FILE__);
|
|
$cmd = $_SERVER['argv'];
|
|
{
|
|
$config = array(
|
|
'exec' => realpath(array_shift($cmd)),
|
|
'phpdbg' => realpath(sprintf(
|
|
'%s/../phpdbg', $cwd
|
|
)),
|
|
'path' => array(
|
|
realpath(dirname(__FILE__))
|
|
),
|
|
'flags' => array(),
|
|
'width' => 75
|
|
);
|
|
|
|
$tests = new Tests($config, $cmd);
|
|
|
|
foreach ($tests->findPaths() as $path) {
|
|
$tests->logPath($path);
|
|
|
|
foreach ($tests->findTests($path) as $test) {
|
|
$tests->logTest($path, $test);
|
|
}
|
|
|
|
$tests->logPathStats($path);
|
|
}
|
|
|
|
$tests->logStats();
|
|
}
|
|
}
|
|
?>
|