1
0
mirror of https://github.com/ezyang/htmlpurifier.git synced 2025-08-05 05:37:49 +02:00

[2.0.1] Revamp error collector scheme: we now have custom mocks and an exchange of responsibilities

- Fix oversight in AutoParagraph dealing with armor.
- Order errors with no line number last
- Language object now needs $config and $context objects to do parameterized objects
- Auto-close notice added
- Token constructors accept line numbers

git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@1245 48356398-32a2-884e-a903-53898d9a118a
This commit is contained in:
Edward Z. Yang
2007-06-26 19:33:37 +00:00
parent 275932ec05
commit 6a95d91a1a
17 changed files with 248 additions and 109 deletions

View File

@@ -0,0 +1,47 @@
<?php
require_once 'HTMLPurifier/ErrorCollector.php';
generate_mock_once('HTMLPurifier_ErrorCollector');
/**
* Extended error collector mock that has the ability to expect context
*/
class HTMLPurifier_ErrorCollectorEMock extends HTMLPurifier_ErrorCollectorMock
{
var $_context;
var $_expected_context = array();
var $_expected_context_at = array();
function prepare(&$context) {
$this->_context =& $context;
}
function expectContext($key, $value) {
$this->_expected_context[$key] = $value;
}
function expectContextAt($step, $key, $value) {
$this->_expected_context_at[$step][$key] = $value;
}
function send() {
// test for context
$test = &$this->_getCurrentTestCase();
foreach ($this->_expected_context as $key => $value) {
$test->assertEqual($value, $this->_context->get($key));
}
$step = $this->getCallCount('send');
if (isset($this->_expected_context_at[$step])) {
foreach ($this->_expected_context_at[$step] as $key => $value) {
$test->assertEqual($value, $this->_context->get($key));
}
}
// boilerplate mock code, does not have return value or references
$args = func_get_args();
$this->_invoke('send', $args);
}
}
?>

View File

@@ -26,7 +26,7 @@ class HTMLPurifier_ErrorCollectorTest extends UnitTestCase
$context->register('Locale', $language);
$context->register('CurrentLine', $line);
$generator = new HTMLPurifier_GeneratorMock();
$generator = new HTMLPurifier_Generator();
$context->register('Generator', $generator);
$collector = new HTMLPurifier_ErrorCollector($context);
@@ -60,7 +60,7 @@ class HTMLPurifier_ErrorCollectorTest extends UnitTestCase
$context = new HTMLPurifier_Context();
$context->register('Locale', $language);
$generator = new HTMLPurifier_GeneratorMock();
$generator = new HTMLPurifier_Generator();
$context->register('Generator', $generator);
$collector = new HTMLPurifier_ErrorCollector($context);
@@ -77,7 +77,7 @@ class HTMLPurifier_ErrorCollectorTest extends UnitTestCase
$context = new HTMLPurifier_Context();
$context->register('Locale', $language);
$generator = new HTMLPurifier_GeneratorMock();
$generator = new HTMLPurifier_Generator();
$context->register('Generator', $generator);
$collector = new HTMLPurifier_ErrorCollector($context);
@@ -100,32 +100,36 @@ class HTMLPurifier_ErrorCollectorTest extends UnitTestCase
function testContextSubstitutions() {
$language = new HTMLPurifier_LanguageMock();
$language->setReturnValue('getMessage',
'$CurrentToken.Name, $CurrentToken.Serialized', array('message-token'));
$language->setReturnValue('getMessage',
'$CurrentAttr.Name => $CurrentAttr.Value', array('message-attr'));
$context = new HTMLPurifier_Context();
$context = new HTMLPurifier_Context();
$context->register('Locale', $language);
$current_token = new HTMLPurifier_Token_Start('a', array('href' => 'http://example.com'));
$current_token->line = 32;
$current_attr = 'href';
$generator = new HTMLPurifier_GeneratorMock();
$generator->setReturnValue('generateFromToken', '<a href="http://example.com">', array($current_token));
$generator = new HTMLPurifier_Generator();
$context->register('Generator', $generator);
$current_token = false;
$context->register('CurrentToken', $current_token);
$collector = new HTMLPurifier_ErrorCollector($context);
$context->register('CurrentToken', $current_token);
$collector->send(E_NOTICE, 'message-token');
// 0
$current_token = new HTMLPurifier_Token_Start('a', array('href' => 'http://example.com'), 32);
$language->setReturnValue('formatMessage', 'Token message',
array('message-data-token', array('CurrentToken' => $current_token)));
$collector->send(E_NOTICE, 'message-data-token');
$current_attr = 'href';
$language->setReturnValue('formatMessage', '$CurrentAttr.Name => $CurrentAttr.Value',
array('message-attr', array('CurrentToken' => $current_token)));
// 1
$collector->send(E_NOTICE, 'message-attr'); // test when context isn't available
// 2
$context->register('CurrentAttr', $current_attr);
$collector->send(E_NOTICE, 'message-attr');
$result = array(
0 => array(32, E_NOTICE, 'a, <a href="http://example.com">'),
0 => array(32, E_NOTICE, 'Token message'),
1 => array(32, E_NOTICE, '$CurrentAttr.Name => $CurrentAttr.Value'),
2 => array(32, E_NOTICE, 'href => http://example.com')
);

View File

@@ -1,6 +1,6 @@
<?php
require_once 'HTMLPurifier/ErrorCollector.php';
require_once 'HTMLPurifier/ErrorCollectorEMock.php';
require_once 'HTMLPurifier/Lexer/DirectLex.php';
class HTMLPurifier_ErrorsHarness extends UnitTestCase
@@ -13,7 +13,8 @@ class HTMLPurifier_ErrorsHarness extends UnitTestCase
$this->config = HTMLPurifier_Config::create(array('Core.CollectErrors' => true));
$this->context = new HTMLPurifier_Context();
generate_mock_once('HTMLPurifier_ErrorCollector');
$this->collector = new HTMLPurifier_ErrorCollectorMock();
$this->collector = new HTMLPurifier_ErrorCollectorEMock();
$this->collector->prepare($this->context);
$this->context->register('ErrorCollector', $this->collector);
}
@@ -22,6 +23,10 @@ class HTMLPurifier_ErrorsHarness extends UnitTestCase
$this->collector->expectOnce('send', $args);
}
function expectContext($key, $value) {
$this->collector->expectContext($key, $value);
}
}
?>

View File

@@ -9,7 +9,9 @@ class HTMLPurifier_LanguageFactoryTest extends UnitTestCase
$factory = HTMLPurifier_LanguageFactory::instance();
$language = $factory->create('en');
$config = HTMLPurifier_Config::create(array('Core.Language' => 'en'));
$context = new HTMLPurifier_Context();
$language = $factory->create($config, $context);
$this->assertIsA($language, 'HTMLPurifier_Language');
$this->assertIdentical($language->code, 'en');
@@ -27,7 +29,10 @@ class HTMLPurifier_LanguageFactoryTest extends UnitTestCase
$factory = HTMLPurifier_LanguageFactory::instance();
$language = $factory->create('en-x-test');
$config = HTMLPurifier_Config::create(array('Core.Language' => 'en-x-test'));
$context = new HTMLPurifier_Context();
$language = $factory->create($config, $context);
$this->assertIsA($language, 'HTMLPurifier_Language_en_x_test');
$this->assertIdentical($language->code, 'en-x-test');

View File

@@ -8,7 +8,9 @@ class HTMLPurifier_LanguageTest extends UnitTestCase
var $lang;
function test_getMessage() {
$lang = new HTMLPurifier_Language();
$config = HTMLPurifier_Config::createDefault();
$context = new HTMLPurifier_Context();
$lang = new HTMLPurifier_Language($config, $context);
$lang->_loaded = true;
$lang->messages['HTMLPurifier'] = 'HTML Purifier';
$this->assertIdentical($lang->getMessage('HTMLPurifier'), 'HTML Purifier');
@@ -16,12 +18,31 @@ class HTMLPurifier_LanguageTest extends UnitTestCase
}
function test_formatMessage() {
$lang = new HTMLPurifier_Language();
$config = HTMLPurifier_Config::createDefault();
$context = new HTMLPurifier_Context();
$lang = new HTMLPurifier_Language($config, $context);
$lang->_loaded = true;
$lang->messages['LanguageTest: Error'] = 'Error is $1 on line $2';
$this->assertIdentical($lang->formatMessage('LanguageTest: Error', array(1=>'fatal', 32)), 'Error is fatal on line 32');
}
function test_formatMessage_complexParameter() {
$config = HTMLPurifier_Config::createDefault();
$context = new HTMLPurifier_Context();
$generator = new HTMLPurifier_Generator(); // replace with mock if this gets icky
$context->register('Generator', $generator);
$lang = new HTMLPurifier_Language($config, $context);
$lang->_loaded = true;
$lang->messages['LanguageTest: Element info'] = 'Element Token: $1.Name, $1.Serialized, $1.Compact, $1.Line';
$lang->messages['LanguageTest: Data info'] = 'Data Token: $1.Data, $1.Serialized, $1.Compact, $1.Line';
$this->assertIdentical($lang->formatMessage('LanguageTest: Element info',
array(1=>new HTMLPurifier_Token_Start('a', array('href'=>'http://example.com'), 18))),
'Element Token: a, <a href="http://example.com">, <a>, 18');
$this->assertIdentical($lang->formatMessage('LanguageTest: Data info',
array(1=>new HTMLPurifier_Token_Text('data>', 23))),
'Data Token: data>, data&gt;, data&gt;, 23');
}
}
?>

View File

@@ -18,19 +18,24 @@ class HTMLPurifier_Lexer_DirectLex_ErrorsTest extends HTMLPurifier_ErrorsHarness
function testUnclosedComment() {
$this->expectErrorCollection(E_WARNING, 'Lexer: Unclosed comment');
$this->expectContext('CurrentLine', 1);
$this->invoke('<!-- >');
}
function testUnescapedLt() {
$this->expectErrorCollection(E_NOTICE, 'Lexer: Unescaped lt');
$this->expectContext('CurrentLine', 1);
$this->invoke('< foo>');
}
function testMissingGt() {
$this->expectErrorCollection(E_WARNING, 'Lexer: Missing gt');
$this->expectContext('CurrentLine', 1);
$this->invoke('<a href=""');
}
// these are sub-errors, will only be thrown in context of collector
function testMissingAttributeKey1() {
$this->expectErrorCollection(E_ERROR, 'Lexer: Missing attribute key');
$this->invokeAttr('=""');

View File

@@ -16,34 +16,44 @@ class HTMLPurifier_Strategy_MakeWellFormed_ErrorsTest extends HTMLPurifier_Strat
}
function testUnnecessaryEndTagRemoved() {
$this->expectErrorCollection(E_WARNING, 'Strategy_MakeWellFormed: Unnecessary end tag removed', 'b');
$this->expectErrorCollection(E_WARNING, 'Strategy_MakeWellFormed: Unnecessary end tag removed');
$this->expectContext('CurrentToken', new HTMLPurifier_Token_End('b', array(), 1));
$this->invoke('</b>');
}
function testUnnecessaryEndTagToText() {
$this->config->set('Core', 'EscapeInvalidTags', true);
$this->expectErrorCollection(E_WARNING, 'Strategy_MakeWellFormed: Unnecessary end tag to text', 'b');
$this->expectErrorCollection(E_WARNING, 'Strategy_MakeWellFormed: Unnecessary end tag to text');
$this->expectContext('CurrentToken', new HTMLPurifier_Token_End('b', array(), 1));
$this->invoke('</b>');
}
function testTagAutoClosed() {
$this->expectErrorCollection(E_NOTICE, 'Strategy_MakeWellFormed: Tag auto closed', new HTMLPurifier_Token_Start('b', array(), 1));
$this->expectContext('CurrentToken', new HTMLPurifier_Token_Start('div', array(), 1));
$this->invoke('<b>Foo<div>Bar</div>');
}
function testStrayEndTagRemoved() {
$this->expectErrorCollection(E_WARNING, 'Strategy_MakeWellFormed: Stray end tag removed', 'b');
$this->expectErrorCollection(E_WARNING, 'Strategy_MakeWellFormed: Stray end tag removed');
$this->expectContext('CurrentToken', new HTMLPurifier_Token_End('b', array(), 1));
$this->invoke('<i></b></i>');
}
function testStrayEndTagToText() {
$this->config->set('Core', 'EscapeInvalidTags', true);
$this->expectErrorCollection(E_WARNING, 'Strategy_MakeWellFormed: Stray end tag to text', 'b');
$this->expectErrorCollection(E_WARNING, 'Strategy_MakeWellFormed: Stray end tag to text');
$this->expectContext('CurrentToken', new HTMLPurifier_Token_End('b', array(), 1));
$this->invoke('<i></b></i>');
}
function testTagClosedByElementEnd() {
$this->expectErrorCollection(E_NOTICE, 'Strategy_MakeWellFormed: Tag closed by element end', 'b');
$this->expectErrorCollection(E_NOTICE, 'Strategy_MakeWellFormed: Tag closed by element end', new HTMLPurifier_Token_Start('b', array(), 1));
$this->invoke('<i><b>Foobar</i>');
}
function testTagClosedByDocumentEnd() {
$this->expectErrorCollection(E_NOTICE, 'Strategy_MakeWellFormed: Tag closed by document end', 'b');
$this->expectErrorCollection(E_NOTICE, 'Strategy_MakeWellFormed: Tag closed by document end', new HTMLPurifier_Token_Start('b', array(), 1));
$this->invoke('<b>Foobar');
}

View File

@@ -16,37 +16,41 @@ class HTMLPurifier_Strategy_RemoveForeignElements_ErrorsTest extends HTMLPurifie
}
function testTagTransform() {
// uses $CurrentToken.Serialized
$this->expectErrorCollection(E_NOTICE, 'Strategy_RemoveForeignElements: Tag transform', 'center');
$this->expectContext('CurrentToken', new HTMLPurifier_Token_Start('div', array('style' => 'text-align:center;'), 1));
$this->invoke('<center>');
}
function testMissingRequiredAttr() {
// a little fragile, since img has two required attributes
$this->expectErrorCollection(E_ERROR, 'Strategy_RemoveForeignElements: Missing required attribute', 'img', 'alt');
$this->expectErrorCollection(E_ERROR, 'Strategy_RemoveForeignElements: Missing required attribute', 'alt');
$this->expectContext('CurrentToken', new HTMLPurifier_Token_Empty('img', array(), 1));
$this->invoke('<img />');
}
function testForeignElementToText() {
// uses $CurrentToken.Serialized
$this->config->set('Core', 'EscapeInvalidTags', true);
$this->expectErrorCollection(E_WARNING, 'Strategy_RemoveForeignElements: Foreign element to text');
$this->invoke('<cannot-possibly-exist-element>');
$this->expectContext('CurrentToken', new HTMLPurifier_Token_Start('invalid', array(), 1));
$this->invoke('<invalid>');
}
function testForeignElementRemoved() {
// uses $CurrentToken.Serialized
$this->expectErrorCollection(E_ERROR, 'Strategy_RemoveForeignElements: Foreign element removed');
$this->invoke('<cannot-possibly-exist-element>');
$this->expectContext('CurrentToken', new HTMLPurifier_Token_Start('invalid', array(), 1));
$this->invoke('<invalid>');
}
function testCommentRemoved() {
$this->expectErrorCollection(E_NOTICE, 'Strategy_RemoveForeignElements: Comment removed', ' test ');
$this->expectErrorCollection(E_NOTICE, 'Strategy_RemoveForeignElements: Comment removed');
$this->expectContext('CurrentToken', new HTMLPurifier_Token_Comment(' test ', 1));
$this->invoke('<!-- test -->');
}
function testScriptRemoved() {
$this->collector->expectAt(0, 'send', array(E_ERROR, 'Strategy_RemoveForeignElements: Script removed'));
$this->collector->expectContextAt(0, 'CurrentToken', new HTMLPurifier_Token_Start('script', array(), 1));
$this->collector->expectAt(1, 'send', array(E_ERROR, 'Strategy_RemoveForeignElements: Token removed to end', 'script'));
$this->invoke('<script>asdf');
}