diff --git a/NEWS b/NEWS index 8e247f58..ac52ad2e 100644 --- a/NEWS +++ b/NEWS @@ -29,6 +29,8 @@ NEWS ( CHANGELOG and HISTORY ) HTMLPurifier Enable by setting %CSS.Proprietary to true. - Colors missing # but in hex form will be corrected - CSS Number algorithm improved +- Unit testing and multi-testing now on steroids: command lines, + XML output, and other goodies now added. . Unit tests for Injector improved . New classes: + HTMLPurifier_AttrDef_CSS_AlphaValue diff --git a/TODO b/TODO index 2fd32372..4964a9ae 100644 --- a/TODO +++ b/TODO @@ -68,8 +68,6 @@ AutoFormat Unknown release (on a scratch-an-itch basis) # CHMOD install script for PEAR installs - # Convert multitest to use the XML format - # Make test/index.php have better cli flags ? Have 'lang' attribute be checked against official lists, achieved by encoding all characters that have string entity equivalents - Abstract ChildDef_BlockQuote to work with all elements that only diff --git a/maintenance/common.php b/maintenance/common.php index c38d351b..af59baf9 100644 --- a/maintenance/common.php +++ b/maintenance/common.php @@ -1,7 +1,5 @@ , Arpad Ray - * @link http://php.net/function.file_put_contents - * @author Aidan Lister - * @version $Revision: 1.27 $ - * @internal resource_context is not supported - * @since PHP 5 - * @require PHP 4.0.0 (user_error) - */ -function php_compat_file_put_contents($filename, $content, $flags = null, $resource_context = null) -{ - // If $content is an array, convert it to a string - if (is_array($content)) { - $content = implode('', $content); - } - - // If we don't have a string, throw an error - if (!is_scalar($content)) { - user_error('file_put_contents() The 2nd parameter should be either a string or an array', - E_USER_WARNING); - return false; - } - - // Get the length of data to write - $length = strlen($content); - - // Check what mode we are using - $mode = ($flags & FILE_APPEND) ? - 'a' : - 'wb'; - - // Check if we're using the include path - $use_inc_path = ($flags & FILE_USE_INCLUDE_PATH) ? - true : - false; - - // Open the file for writing - if (($fh = @fopen($filename, $mode, $use_inc_path)) === false) { - user_error('file_put_contents() failed to open stream: Permission denied', - E_USER_WARNING); - return false; - } - - // Attempt to get an exclusive lock - $use_lock = ($flags & LOCK_EX) ? true : false ; - if ($use_lock === true) { - if (!flock($fh, LOCK_EX)) { - return false; - } - } - - // Write to the file - $bytes = 0; - if (($bytes = @fwrite($fh, $content)) === false) { - $errormsg = sprintf('file_put_contents() Failed to write %d bytes to %s', - $length, - $filename); - user_error($errormsg, E_USER_WARNING); - return false; - } - - // Close the handle - @fclose($fh); - - // Check all the data was written - if ($bytes != $length) { - $errormsg = sprintf('file_put_contents() Only %d of %d bytes written, possibly out of free disk space.', - $bytes, - $length); - user_error($errormsg, E_USER_WARNING); - return false; - } - - // Return length - return $bytes; -} - - -// Define -if (!function_exists('file_put_contents')) { - function file_put_contents($filename, $content, $flags = null, $resource_context = null) - { - return php_compat_file_put_contents($filename, $content, $flags, $resource_context); - } -} diff --git a/maintenance/flush-definition-cache.php b/maintenance/flush-definition-cache.php index 93af3b75..22037620 100755 --- a/maintenance/flush-definition-cache.php +++ b/maintenance/flush-definition-cache.php @@ -1,6 +1,7 @@ #!/usr/bin/php _command = $command; + $this->_quiet = $quiet; + } + public function getLabel() { + return $this->_command; + } + public function run(&$reporter) { + if (!$this->_quiet) $reporter->paintFormattedMessage('Running ['.$this->_command.']'); + $xml = shell_exec($this->_command); + if (! $xml) { + if (!$this->_quiet) { + trigger_error('Command did not have any output [' . $this->_command . ']'); + } + return false; + } + $parser = &$this->_createParser($reporter); + + set_error_handler(array($this, '_errorHandler')); + $status = $parser->parse($xml); + restore_error_handler(); + + if (! $status) { + if (!$this->_quiet) { + foreach ($this->_errors as $error) { + list($no, $str, $file, $line) = $error; + $reporter->paintFormattedMessage("Error $no: $str on line $line of $file"); + } + $msg = "Command produced malformed XML: \n"; + if (strlen($xml) > 120) { + $msg .= substr($xml, 0, 50) . "...\n\n[snip]\n\n..." . substr($xml, -50); + } else { + $msg .= $xml; + } + $reporter->paintFormattedMessage($msg); + } + return false; + } + return true; + } + public function &_createParser(&$reporter) { + $parser = new SimpleTestXmlParser($reporter); + return $parser; + } + public function getSize() { + return 1; // we don't know it + } + public function _errorHandler($a, $b, $c, $d) { + $this->_errors[] = array($a, $b, $c, $d); // see set_error_handler() + } +} + diff --git a/tests/HTMLPurifier/EncoderTest.php b/tests/HTMLPurifier/EncoderTest.php index 33efd31f..8af0e66d 100644 --- a/tests/HTMLPurifier/EncoderTest.php +++ b/tests/HTMLPurifier/EncoderTest.php @@ -35,7 +35,8 @@ class HTMLPurifier_EncoderTest extends HTMLPurifier_Harness // UTF-8 means that we don't touch it $this->assertIdentical( HTMLPurifier_Encoder::convertToUTF8("\xF6", $config, $context), - "\xF6" // this is invalid + "\xF6", // this is invalid + 'Expected identical [Binary: F6]' ); $this->assertNoErrors(); @@ -80,7 +81,8 @@ class HTMLPurifier_EncoderTest extends HTMLPurifier_Harness // Now it gets converted $this->assertIdentical( HTMLPurifier_Encoder::convertFromUTF8("\xC3\xB6", $config, $context), - "\xF6" + "\xF6", + 'Expected identical [Binary: F6]' ); if (function_exists('iconv')) { @@ -98,7 +100,8 @@ class HTMLPurifier_EncoderTest extends HTMLPurifier_Harness )); $this->assertIdentical( HTMLPurifier_Encoder::convertFromUTF8("\xC3\xB6", $config, $context), - "\xF6" + "\xF6", + 'Expected identical [Binary: F6]' ); $this->assertIdentical( diff --git a/tests/HTMLPurifier/EntityParserTest.php b/tests/HTMLPurifier/EntityParserTest.php index 2acfa8d3..e41006ce 100644 --- a/tests/HTMLPurifier/EntityParserTest.php +++ b/tests/HTMLPurifier/EntityParserTest.php @@ -69,7 +69,7 @@ class HTMLPurifier_EntityParserTest extends HTMLPurifier_Harness $this->assertIdentical( $this->EntityParser->substituteNonSpecialEntities($string), $expect, - $arg[0] . ': %s' + 'Identical expectation [Hex: '. dechex($arg[0]) .']' ); } diff --git a/tests/HTMLPurifier/SimpleTest/Reporter.php b/tests/HTMLPurifier/SimpleTest/Reporter.php index 5f01d804..5ab31f0b 100644 --- a/tests/HTMLPurifier/SimpleTest/Reporter.php +++ b/tests/HTMLPurifier/SimpleTest/Reporter.php @@ -3,27 +3,33 @@ class HTMLPurifier_SimpleTest_Reporter extends HTMLReporter { - function paintHeader($test_name) { + protected $ac; + + public function __construct($encoding, $ac) { + $this->ac = $ac; + parent::HTMLReporter($encoding); + } + + public function paintHeader($test_name) { parent::paintHeader($test_name); - $test_file = $GLOBALS['HTMLPurifierTest']['File']; ?>
- /> + ac['standalone']) {echo 'checked="checked" ';} ?>/>
default value. Depending on the type of the default value, + * arguments will be typecast accordingly. For example, if + * 'flag' => false is passed, all arguments for that will be cast to + * boolean. Do *not* pass null, as it will not be recognized. + * @param $aliases + * + */ +function htmlpurifier_parse_args(&$AC, $aliases) { + if (empty($_GET)) { + array_shift($_SERVER['argv']); + foreach ($_SERVER['argv'] as $opt) { + if (strpos($opt, "=") !== false) { + list($o, $v) = explode("=", $opt, 2); + } else { + $o = $opt; + $v = true; + } + $o = ltrim($o, '-'); + htmlpurifier_args($AC, $aliases, $o, $v); + } + } else { + foreach ($_GET as $o => $v) { + if (get_magic_quotes_gpc()) $v = stripslashes($v); + htmlpurifier_args($AC, $aliases, $o, $v); + } + } +} + +/** + * Actually performs assignment to $AC, see htmlpurifier_parse_args() + * @param $AC Arguments array to write to + * @param $aliases Aliases for options + * @param $o Argument name + * @param $v Argument value + */ +function htmlpurifier_args(&$AC, $aliases, $o, $v) { + if (isset($aliases[$o])) $o = $aliases[$o]; + if (!isset($AC[$o])) return; + if (is_string($AC[$o])) $AC[$o] = $v; + if (is_bool($AC[$o])) $AC[$o] = true; +} diff --git a/tests/index.php b/tests/index.php index f225ba2c..50cf7985 100755 --- a/tests/index.php +++ b/tests/index.php @@ -1,19 +1,38 @@ 'file', +); +htmlpurifier_parse_args($AC, $aliases); + // clean out cache if necessary -if (isset($_GET['flush'])) shell_exec('php ../maintenance/flush-definition-cache.php'); +if ($AC['flush']) shell_exec('php ../maintenance/flush-definition-cache.php'); // initialize and load HTML Purifier // use ?standalone to load the alterative standalone stub -if (isset($_GET['standalone']) || (isset($argv[1]) && $argv[1] == 'standalone')) { +if ($AC['standalone']) { set_include_path(realpath('blanks') . PATH_SEPARATOR . get_include_path()); require_once '../library/HTMLPurifier.standalone.php'; } else { @@ -33,25 +52,25 @@ $GLOBALS['HTMLPurifierTest']['Files'] = $test_files; // for the reporter $test_file_lookup = array_flip($test_files); // determine test file -if (isset($_GET['f']) && isset($test_file_lookup[$_GET['f']])) { - $GLOBALS['HTMLPurifierTest']['File'] = $_GET['f']; -} elseif (isset($argv[1]) && isset($test_file_lookup[$argv[1]])) { - // command-line - $GLOBALS['HTMLPurifierTest']['File'] = $argv[1]; -} else { - $GLOBALS['HTMLPurifierTest']['File'] = false; +if ($AC['file']) { + if (!isset($test_file_lookup[$AC['file']])) { + echo "Invalid file passed\n"; + exit; + } } // we can't use addTestFile because SimpleTest chokes on E_STRICT warnings -if ($test_file = $GLOBALS['HTMLPurifierTest']['File']) { +if ($AC['file']) { - $test = new GroupTest($test_file); - require_once $test_file; - $test->addTestClass(path2class($test_file)); + $test = new TestSuite($AC['file']); + require_once $AC['file']; + $test->addTestClass(path2class($AC['file'])); } else { - $test = new GroupTest('All HTML Purifier tests on PHP ' . PHP_VERSION); + $standalone = ''; + if ($AC['standalone']) $standalone = ' (standalone)'; + $test = new TestSuite('All HTML Purifier tests on PHP ' . PHP_VERSION . $standalone); foreach ($test_files as $test_file) { require_once $test_file; $test->addTestClass(path2class($test_file)); @@ -59,7 +78,13 @@ if ($test_file = $GLOBALS['HTMLPurifierTest']['File']) { } -if (SimpleReporter::inCli()) $reporter = new TextReporter(); -else $reporter = new HTMLPurifier_SimpleTest_Reporter('UTF-8'); +if ($AC['xml']) { + if (!SimpleReporter::inCli()) header('Content-Type: text/xml;charset=UTF-8'); + $reporter = new XmlReporter(); +} elseif (SimpleReporter::inCli()) { + $reporter = new TextReporter(); +} else { + $reporter = new HTMLPurifier_SimpleTest_Reporter('UTF-8', $AC); +} $test->run($reporter); diff --git a/tests/multitest.php b/tests/multitest.php index 2b07011b..69116bad 100644 --- a/tests/multitest.php +++ b/tests/multitest.php @@ -3,49 +3,77 @@ /** @file * Multiple PHP Versions test * - * This file tests HTML Purifier in all versions of PHP. It requires a - * script called phpv that takes an extra argument, $version, before - * the filename, is required. Contact me if you'd like to set up a - * similar script. + * This file tests HTML Purifier in all versions of PHP. Arguments + * are specified like --arg=opt, allowed arguments are: + * - exclude-normal, excludes normal tests + * - exclude-standalone, excludes standalone tests + * - file (f), specifies a single file to test for all versions + * - xml, if specified output is XML + * - quiet (q), if specified no informative messages are enabled (please use + * this if you're outputting XML) + * + * @note + * It requires a script called phpv that takes an extra argument (the + * version number of PHP) before all other arguments. Contact me if you'd + * like to set up a similar script. The name of the script can be + * edited with $phpv + * + * @note + * Also, configuration must be set up with a variable called + * $versions_to_test specifying version numbers to pass to $phpv */ -$versions_to_test = array( - 'FLUSH', - '5.0.0', - '5.0.1', - '5.0.2', - '5.0.3', - '5.0.4', - '5.0.5', - '5.1.0', - '5.1.1', - '5.1.2', - '5.1.3', - '5.1.4', - // '5.1.5', // zip appears to be missing - '5.1.6', - '5.2.0', - '5.2.1', - '5.2.2', - '5.2.3', - '5.2.4', - '5.2.5', - '5.3.0-dev', - // '6.0.0-dev', -); +define('HTMLPurifierTest', 1); +require_once 'common.php'; -echo str_repeat('-', 70) . "\n"; -echo "HTML Purifier\n"; -echo "Multiple PHP Versions Test\n\n"; - -passthru("php ../maintenance/merge-library.php"); - -foreach ($versions_to_test as $version) { - if ($version === 'FLUSH') { - shell_exec('php ../maintenance/flush-definition-cache.php'); - continue; - } - passthru("phpv $version index.php"); - passthru("phpv $version index.php standalone"); - echo "\n\n"; +if (!SimpleReporter::inCli()) { + echo 'Multitest only available from command line'; + exit; } + +$AC = array(); // parameters +$AC['exclude-normal'] = false; +$AC['exclude-standalone'] = false; +$AC['file'] = ''; +$AC['xml'] = false; +$AC['quiet'] = false; +$aliases = array( + 'f' => 'file', + 'q' => 'quiet', +); +htmlpurifier_parse_args($AC, $aliases); + +shell_exec("php ../maintenance/merge-library.php"); +shell_exec('php ../maintenance/flush-definition-cache.php'); + +$test = new TestSuite('HTML Purifier Multiple Versions Test'); +$file = ''; +if ($AC['file']) { + $test_files = array(); + require 'test_files.php'; + $test_files_lookup = array_flip($test_files); + if (isset($test_files_lookup[$AC['file']])) { + $file = '--file=' . $AC['file']; + } else { + echo "Invalid file passed\n"; + exit; + } +} +foreach ($versions_to_test as $version) { + $flush = ''; + if (is_array($version)) { + $version = $version[0]; + $flush = '--flush'; + } + if (!$AC['exclude-normal']) $test->addTestCase(new CliTestCase("$phpv $version index.php --xml $flush $file", $AC['quiet'])); + if (!$AC['exclude-standalone']) $test->addTestCase(new CliTestCase("$phpv $version index.php --xml --standalone $file", $AC['quiet'])); +} + +if ($AC['xml']) { + $reporter = new XmlReporter(); +} else { + $reporter = new TextReporter(); +} +$test->run($reporter); + +shell_exec('php ../maintenance/flush-definition-cache.php');