';
+ }
+
+ public function addScope($id = false) {
+ if ($id == false) {
+ $id = $this->scope_nextID++;
+ }
+ $this->current_scopes[$id] = true;
+ }
+
+ public function removeScope($id) {
+ if (isset($this->current_scopes[$id])) unset($this->current_scopes[$id]);
+ }
+
+ public function resetScopes() {
+ $this->current_scopes = array();
+ $this->scope_nextID = 1;
+ }
+
+ public function isInScopes($scopes = array()) {
+ if (empty($this->current_scopes)) {
+ return false;
+ }
+ if (!is_array($scopes)) {
+ $scopes = array($scopes);
+ }
+ foreach ($scopes as $scope_id) {
+ if (empty($this->current_scopes[$scope_id])) {
+ return false;
+ }
+ }
+ if (empty($scopes)) {
+ if ($this->scope_nextID == 1) {
+ return false;
+ }
+ for($i = 1; $i < $this->scope_nextID; $i++) {
+ if (empty($this->current_scopes[$i])) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+}
+
+// vim: et sw=4 sts=4
diff --git a/lib/htmlpurifier/tests/FSTools/FileSystemHarness.php b/lib/htmlpurifier/tests/FSTools/FileSystemHarness.php
new file mode 100644
index 000000000..710196e4f
--- /dev/null
+++ b/lib/htmlpurifier/tests/FSTools/FileSystemHarness.php
@@ -0,0 +1,36 @@
+dir = 'tmp/' . md5(uniqid(rand(), true)) . '/';
+ mkdir($this->dir);
+ $this->oldDir = getcwd();
+
+ }
+
+ function __destruct() {
+ FSTools::singleton()->rmdirr($this->dir);
+ }
+
+ function setup() {
+ chdir($this->dir);
+ }
+
+ function tearDown() {
+ chdir($this->oldDir);
+ }
+
+}
+
+// vim: et sw=4 sts=4
diff --git a/lib/htmlpurifier/tests/FSTools/FileTest.php b/lib/htmlpurifier/tests/FSTools/FileTest.php
new file mode 100644
index 000000000..e9b703a2d
--- /dev/null
+++ b/lib/htmlpurifier/tests/FSTools/FileTest.php
@@ -0,0 +1,46 @@
+assertFalse($file->exists());
+ $file->write('foobar');
+ $this->assertTrue($file->exists());
+ $this->assertEqual($file->get(), 'foobar');
+ $file->delete();
+ $this->assertFalse($file->exists());
+ }
+
+ function testGetNonExistent() {
+ $name = 'notfound.txt';
+ $file = new FSTools_File($name);
+ $this->expectError();
+ $this->assertFalse($file->get());
+ }
+
+ function testHandle() {
+ $file = new FSTools_File('foo.txt');
+ $this->assertFalse($file->exists());
+ $file->open('w');
+ $this->assertTrue($file->exists());
+ $file->put('Foobar');
+ $file->close();
+ $file->open('r');
+ $this->assertIdentical('F', $file->getChar());
+ $this->assertFalse($file->eof());
+ $this->assertIdentical('oo', $file->read(2));
+ $this->assertIdentical('bar', $file->getLine());
+ $this->assertTrue($file->eof());
+ }
+
+}
+
+// vim: et sw=4 sts=4
diff --git a/lib/htmlpurifier/tests/HTMLPurifier/AttrCollectionsTest.php b/lib/htmlpurifier/tests/HTMLPurifier/AttrCollectionsTest.php
new file mode 100644
index 000000000..227bc9535
--- /dev/null
+++ b/lib/htmlpurifier/tests/HTMLPurifier/AttrCollectionsTest.php
@@ -0,0 +1,134 @@
+attr_collections = array(
+ 'Core' => array(
+ 0 => array('Soup', 'Undefined'),
+ 'attribute' => 'Type',
+ 'attribute-2' => 'Type2',
+ ),
+ 'Soup' => array(
+ 'attribute-3' => 'Type3-old' // overwritten
+ )
+ );
+
+ $modules['Module2'] = new HTMLPurifier_HTMLModule();
+ $modules['Module2']->attr_collections = array(
+ 'Core' => array(
+ 0 => array('Brocolli')
+ ),
+ 'Soup' => array(
+ 'attribute-3' => 'Type3'
+ ),
+ 'Brocolli' => array()
+ );
+
+ $collections->__construct($types, $modules);
+ // this is without identifier expansion or inclusions
+ $this->assertIdentical(
+ $collections->info,
+ array(
+ 'Core' => array(
+ 0 => array('Soup', 'Undefined', 'Brocolli'),
+ 'attribute' => 'Type',
+ 'attribute-2' => 'Type2'
+ ),
+ 'Soup' => array(
+ 'attribute-3' => 'Type3'
+ ),
+ 'Brocolli' => array()
+ )
+ );
+
+ }
+
+ function test_performInclusions() {
+
+ generate_mock_once('HTMLPurifier_AttrTypes');
+
+ $types = new HTMLPurifier_AttrTypesMock();
+ $collections = new HTMLPurifier_AttrCollections($types, array());
+ $collections->info = array(
+ 'Core' => array(0 => array('Inclusion', 'Undefined'), 'attr-original' => 'Type'),
+ 'Inclusion' => array(0 => array('SubInclusion'), 'attr' => 'Type'),
+ 'SubInclusion' => array('attr2' => 'Type')
+ );
+
+ $collections->performInclusions($collections->info['Core']);
+ $this->assertIdentical(
+ $collections->info['Core'],
+ array(
+ 'attr-original' => 'Type',
+ 'attr' => 'Type',
+ 'attr2' => 'Type'
+ )
+ );
+
+ // test recursive
+ $collections->info = array(
+ 'One' => array(0 => array('Two'), 'one' => 'Type'),
+ 'Two' => array(0 => array('One'), 'two' => 'Type')
+ );
+ $collections->performInclusions($collections->info['One']);
+ $this->assertIdentical(
+ $collections->info['One'],
+ array(
+ 'one' => 'Type',
+ 'two' => 'Type'
+ )
+ );
+
+ }
+
+ function test_expandIdentifiers() {
+
+ generate_mock_once('HTMLPurifier_AttrTypes');
+
+ $types = new HTMLPurifier_AttrTypesMock();
+ $collections = new HTMLPurifier_AttrCollections($types, array());
+
+ $attr = array(
+ 'attr1' => 'Color',
+ 'attr2*' => 'URI'
+ );
+ $c_object = new HTMLPurifier_AttrDef_HTML_Color();
+ $u_object = new HTMLPurifier_AttrDef_URI();
+
+ $types->setReturnValue('get', $c_object, array('Color'));
+ $types->setReturnValue('get', $u_object, array('URI'));
+
+ $collections->expandIdentifiers($attr, $types);
+
+ $u_object->required = true;
+ $this->assertIdentical(
+ $attr,
+ array(
+ 'attr1' => $c_object,
+ 'attr2' => $u_object
+ )
+ );
+
+ }
+
+}
+
+// vim: et sw=4 sts=4
diff --git a/lib/htmlpurifier/tests/HTMLPurifier/AttrDef/CSS/AlphaValueTest.php b/lib/htmlpurifier/tests/HTMLPurifier/AttrDef/CSS/AlphaValueTest.php
new file mode 100644
index 000000000..56efa306f
--- /dev/null
+++ b/lib/htmlpurifier/tests/HTMLPurifier/AttrDef/CSS/AlphaValueTest.php
@@ -0,0 +1,28 @@
+def = new HTMLPurifier_AttrDef_CSS_AlphaValue();
+
+ $this->assertDef('0');
+ $this->assertDef('1');
+ $this->assertDef('.2');
+
+ // clamping to [0.0, 1,0]
+ $this->assertDef('1.2', '1');
+ $this->assertDef('-3', '0');
+
+ $this->assertDef('0.0', '0');
+ $this->assertDef('1.0', '1');
+ $this->assertDef('000', '0');
+
+ $this->assertDef('asdf', false);
+
+ }
+
+}
+
+// vim: et sw=4 sts=4
diff --git a/lib/htmlpurifier/tests/HTMLPurifier/AttrDef/CSS/BackgroundPositionTest.php b/lib/htmlpurifier/tests/HTMLPurifier/AttrDef/CSS/BackgroundPositionTest.php
new file mode 100644
index 000000000..a216b2677
--- /dev/null
+++ b/lib/htmlpurifier/tests/HTMLPurifier/AttrDef/CSS/BackgroundPositionTest.php
@@ -0,0 +1,68 @@
+def = new HTMLPurifier_AttrDef_CSS_BackgroundPosition();
+
+ // explicitly cited in spec
+ $this->assertDef('0% 0%');
+ $this->assertDef('100% 100%');
+ $this->assertDef('14% 84%');
+ $this->assertDef('2cm 1cm');
+ $this->assertDef('top');
+ $this->assertDef('left');
+ $this->assertDef('center');
+ $this->assertDef('right');
+ $this->assertDef('bottom');
+ $this->assertDef('left top');
+ $this->assertDef('center top');
+ $this->assertDef('right top');
+ $this->assertDef('left center');
+ $this->assertDef('right center');
+ $this->assertDef('left bottom');
+ $this->assertDef('center bottom');
+ $this->assertDef('right bottom');
+
+ // reordered due to internal impl details
+ $this->assertDef('top left', 'left top');
+ $this->assertDef('top center', 'top');
+ $this->assertDef('top right', 'right top');
+ $this->assertDef('center left', 'left');
+ $this->assertDef('center center', 'center');
+ $this->assertDef('center right', 'right');
+ $this->assertDef('bottom left', 'left bottom');
+ $this->assertDef('bottom center', 'bottom');
+ $this->assertDef('bottom right', 'right bottom');
+
+ // more cases from the defined syntax
+ $this->assertDef('1.32in 4ex');
+ $this->assertDef('-14% -84.65%');
+ $this->assertDef('-1in -4ex');
+ $this->assertDef('-1pc 2.3%');
+
+ // keyword mixing
+ $this->assertDef('3em top');
+ $this->assertDef('left 50%');
+
+ // fixable keyword mixing
+ $this->assertDef('top 3em', '3em top');
+ $this->assertDef('50% left', 'left 50%');
+
+ // whitespace collapsing
+ $this->assertDef('3em top', '3em top');
+ $this->assertDef("left\n \t foo ", 'left');
+
+ // invalid uses (we're going to be strict on these)
+ $this->assertDef('foo bar', false);
+ $this->assertDef('left left', 'left');
+ $this->assertDef('left right top bottom center left', 'left bottom');
+ $this->assertDef('0fr 9%', '9%');
+
+ }
+
+}
+
+// vim: et sw=4 sts=4
diff --git a/lib/htmlpurifier/tests/HTMLPurifier/AttrDef/CSS/BackgroundTest.php b/lib/htmlpurifier/tests/HTMLPurifier/AttrDef/CSS/BackgroundTest.php
new file mode 100644
index 000000000..83461c365
--- /dev/null
+++ b/lib/htmlpurifier/tests/HTMLPurifier/AttrDef/CSS/BackgroundTest.php
@@ -0,0 +1,23 @@
+def = new HTMLPurifier_AttrDef_CSS_Background($config);
+
+ $valid = '#333 url("chess.png") repeat fixed 50% top';
+ $this->assertDef($valid);
+ $this->assertDef('url(\'chess.png\') #333 50% top repeat fixed', $valid);
+ $this->assertDef(
+ 'rgb(34, 56, 33) url(chess.png) repeat fixed top',
+ 'rgb(34,56,33) url("chess.png") repeat fixed top'
+ );
+
+ }
+
+}
+
+// vim: et sw=4 sts=4
diff --git a/lib/htmlpurifier/tests/HTMLPurifier/AttrDef/CSS/BorderTest.php b/lib/htmlpurifier/tests/HTMLPurifier/AttrDef/CSS/BorderTest.php
new file mode 100644
index 000000000..6cd77fd7a
--- /dev/null
+++ b/lib/htmlpurifier/tests/HTMLPurifier/AttrDef/CSS/BorderTest.php
@@ -0,0 +1,21 @@
+def = new HTMLPurifier_AttrDef_CSS_Border($config);
+
+ $this->assertDef('thick solid red', 'thick solid #FF0000');
+ $this->assertDef('thick solid');
+ $this->assertDef('solid red', 'solid #FF0000');
+ $this->assertDef('1px solid #000');
+ $this->assertDef('1px solid rgb(0, 0, 0)', '1px solid rgb(0,0,0)');
+
+ }
+
+}
+
+// vim: et sw=4 sts=4
diff --git a/lib/htmlpurifier/tests/HTMLPurifier/AttrDef/CSS/ColorTest.php b/lib/htmlpurifier/tests/HTMLPurifier/AttrDef/CSS/ColorTest.php
new file mode 100644
index 000000000..f3a74e897
--- /dev/null
+++ b/lib/htmlpurifier/tests/HTMLPurifier/AttrDef/CSS/ColorTest.php
@@ -0,0 +1,41 @@
+def = new HTMLPurifier_AttrDef_CSS_Color();
+
+ $this->assertDef('#F00');
+ $this->assertDef('#fff');
+ $this->assertDef('#eeeeee');
+ $this->assertDef('#808080');
+ $this->assertDef('rgb(255, 0, 0)', 'rgb(255,0,0)'); // rm spaces
+ $this->assertDef('rgb(100%,0%,0%)');
+ $this->assertDef('rgb(50.5%,23.2%,43.9%)'); // decimals okay
+
+ $this->assertDef('#G00', false);
+ $this->assertDef('cmyk(40, 23, 43, 23)', false);
+ $this->assertDef('rgb(0%, 23, 68%)', false);
+
+ // clip numbers outside sRGB gamut
+ $this->assertDef('rgb(200%, -10%, 0%)', 'rgb(100%,0%,0%)');
+ $this->assertDef('rgb(256,-23,34)', 'rgb(255,0,34)');
+
+ // color keywords, of course
+ $this->assertDef('red', '#FF0000');
+
+ // malformed hex declaration
+ $this->assertDef('808080', '#808080');
+ $this->assertDef('000000', '#000000');
+ $this->assertDef('fed', '#fed');
+
+ // maybe hex transformations would be another nice feature
+ // at the very least transform rgb percent to rgb integer
+
+ }
+
+}
+
+// vim: et sw=4 sts=4
diff --git a/lib/htmlpurifier/tests/HTMLPurifier/AttrDef/CSS/CompositeTest.php b/lib/htmlpurifier/tests/HTMLPurifier/AttrDef/CSS/CompositeTest.php
new file mode 100644
index 000000000..44bef5551
--- /dev/null
+++ b/lib/htmlpurifier/tests/HTMLPurifier/AttrDef/CSS/CompositeTest.php
@@ -0,0 +1,81 @@
+defs =& $defs;
+ }
+
+}
+
+class HTMLPurifier_AttrDef_CSS_CompositeTest extends HTMLPurifier_AttrDefHarness
+{
+
+ protected $def1, $def2;
+
+ function test() {
+
+ generate_mock_once('HTMLPurifier_AttrDef');
+
+ $config = HTMLPurifier_Config::createDefault();
+ $context = new HTMLPurifier_Context();
+
+ // first test: value properly validates on first definition
+ // so second def is never called
+
+ $def1 = new HTMLPurifier_AttrDefMock();
+ $def2 = new HTMLPurifier_AttrDefMock();
+ $defs = array(&$def1, &$def2);
+ $def = new HTMLPurifier_AttrDef_CSS_Composite_Testable($defs);
+ $input = 'FOOBAR';
+ $output = 'foobar';
+ $def1_params = array($input, $config, $context);
+ $def1->expectOnce('validate', $def1_params);
+ $def1->setReturnValue('validate', $output, $def1_params);
+ $def2->expectNever('validate');
+
+ $result = $def->validate($input, $config, $context);
+ $this->assertIdentical($output, $result);
+
+ // second test, first def fails, second def works
+
+ $def1 = new HTMLPurifier_AttrDefMock();
+ $def2 = new HTMLPurifier_AttrDefMock();
+ $defs = array(&$def1, &$def2);
+ $def = new HTMLPurifier_AttrDef_CSS_Composite_Testable($defs);
+ $input = 'BOOMA';
+ $output = 'booma';
+ $def_params = array($input, $config, $context);
+ $def1->expectOnce('validate', $def_params);
+ $def1->setReturnValue('validate', false, $def_params);
+ $def2->expectOnce('validate', $def_params);
+ $def2->setReturnValue('validate', $output, $def_params);
+
+ $result = $def->validate($input, $config, $context);
+ $this->assertIdentical($output, $result);
+
+ // third test, all fail, so composite faiils
+
+ $def1 = new HTMLPurifier_AttrDefMock();
+ $def2 = new HTMLPurifier_AttrDefMock();
+ $defs = array(&$def1, &$def2);
+ $def = new HTMLPurifier_AttrDef_CSS_Composite_Testable($defs);
+ $input = 'BOOMA';
+ $output = false;
+ $def_params = array($input, $config, $context);
+ $def1->expectOnce('validate', $def_params);
+ $def1->setReturnValue('validate', false, $def_params);
+ $def2->expectOnce('validate', $def_params);
+ $def2->setReturnValue('validate', false, $def_params);
+
+ $result = $def->validate($input, $config, $context);
+ $this->assertIdentical($output, $result);
+
+ }
+
+}
+
+// vim: et sw=4 sts=4
diff --git a/lib/htmlpurifier/tests/HTMLPurifier/AttrDef/CSS/FilterTest.php b/lib/htmlpurifier/tests/HTMLPurifier/AttrDef/CSS/FilterTest.php
new file mode 100644
index 000000000..7795643f1
--- /dev/null
+++ b/lib/htmlpurifier/tests/HTMLPurifier/AttrDef/CSS/FilterTest.php
@@ -0,0 +1,29 @@
+def = new HTMLPurifier_AttrDef_CSS_Filter();
+
+ $this->assertDef('none');
+
+ $this->assertDef('alpha(opacity=0)');
+ $this->assertDef('alpha(opacity=100)');
+ $this->assertDef('alpha(opacity=50)');
+ $this->assertDef('alpha(opacity=342)', 'alpha(opacity=100)');
+ $this->assertDef('alpha(opacity=-23)', 'alpha(opacity=0)');
+
+ $this->assertDef('alpha ( opacity = 0 )', 'alpha(opacity=0)');
+ $this->assertDef('alpha(opacity=0,opacity=100)', 'alpha(opacity=0)');
+
+ $this->assertDef('progid:DXImageTransform.Microsoft.Alpha(opacity=20)');
+
+ $this->assertDef('progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)', false);
+
+ }
+
+}
+
+// vim: et sw=4 sts=4
diff --git a/lib/htmlpurifier/tests/HTMLPurifier/AttrDef/CSS/FontFamilyTest.php b/lib/htmlpurifier/tests/HTMLPurifier/AttrDef/CSS/FontFamilyTest.php
new file mode 100644
index 000000000..fda8e01ff
--- /dev/null
+++ b/lib/htmlpurifier/tests/HTMLPurifier/AttrDef/CSS/FontFamilyTest.php
@@ -0,0 +1,52 @@
+def = new HTMLPurifier_AttrDef_CSS_FontFamily();
+
+ $this->assertDef('Gill, Helvetica, sans-serif');
+ $this->assertDef("'Times New Roman', serif");
+ $this->assertDef("\"Times New Roman\"", "'Times New Roman'");
+ $this->assertDef('01234');
+ $this->assertDef(',', false);
+ $this->assertDef('Times New Roman, serif', "'Times New Roman', serif");
+ $this->assertDef($d = "'\xE5\xAE\x8B\xE4\xBD\x93'");
+ $this->assertDef("\xE5\xAE\x8B\xE4\xBD\x93", $d);
+ $this->assertDef("'\\01'", "''");
+ $this->assertDef("'\\20'", "' '");
+ $this->assertDef("\\0020", "' '");
+ $this->assertDef("'\\000045'", "E");
+ $this->assertDef("','", false);
+ $this->assertDef("',' foobar','", "' foobar'");
+ $this->assertDef("'\\000045a'", "Ea");
+ $this->assertDef("'\\00045 a'", "Ea");
+ $this->assertDef("'\\00045 a'", "'E a'");
+ $this->assertDef("'\\\nf'", "f");
+ // No longer supported, except maybe in NoJS mode (see source
+ // file for more explanation)
+ //$this->assertDef($d = '"John\'s Font"');
+ //$this->assertDef("John's Font", $d);
+ //$this->assertDef("'\\','f'", "\"\\5C \", f");
+ //$this->assertDef("'\\27'", "\"'\"");
+ //$this->assertDef('"\\22"', "\"\\22 \"");
+ //$this->assertDef('"\\""', "\"\\22 \"");
+ //$this->assertDef('"\'"', "\"'\"");
+ }
+
+ function testAllowed() {
+ $this->config->set('CSS.AllowedFonts', array('serif', 'Times New Roman'));
+
+ $this->assertDef('serif');
+ $this->assertDef('sans-serif', false);
+ $this->assertDef('serif, sans-serif', 'serif');
+ $this->assertDef('Times New Roman', "'Times New Roman'");
+ $this->assertDef("'Times New Roman'");
+ $this->assertDef('foo', false);
+ }
+
+}
+
+// vim: et sw=4 sts=4
diff --git a/lib/htmlpurifier/tests/HTMLPurifier/AttrDef/CSS/FontTest.php b/lib/htmlpurifier/tests/HTMLPurifier/AttrDef/CSS/FontTest.php
new file mode 100644
index 000000000..91870d13e
--- /dev/null
+++ b/lib/htmlpurifier/tests/HTMLPurifier/AttrDef/CSS/FontTest.php
@@ -0,0 +1,34 @@
+def = new HTMLPurifier_AttrDef_CSS_Font($config);
+
+ // hodgepodge of usage cases from W3C spec, but " -> '
+ $this->assertDef('12px/14px sans-serif');
+ $this->assertDef('80% sans-serif');
+ $this->assertDef("x-large/110% 'New Century Schoolbook', serif");
+ $this->assertDef('bold italic large Palatino, serif');
+ $this->assertDef('normal small-caps 120%/120% fantasy');
+ $this->assertDef("300 italic 1.3em/1.7em 'FB Armada', sans-serif");
+ $this->assertDef('600 9px Charcoal');
+ $this->assertDef('600 9px/ 12px Charcoal', '600 9px/12px Charcoal');
+
+ // spacing
+ $this->assertDef('12px / 14px sans-serif', '12px/14px sans-serif');
+
+ // system fonts
+ $this->assertDef('menu');
+
+ $this->assertDef('800', false);
+ $this->assertDef('600 9px//12px Charcoal', false);
+
+ }
+
+}
+
+// vim: et sw=4 sts=4
diff --git a/lib/htmlpurifier/tests/HTMLPurifier/AttrDef/CSS/ImportantDecoratorTest.php b/lib/htmlpurifier/tests/HTMLPurifier/AttrDef/CSS/ImportantDecoratorTest.php
new file mode 100644
index 000000000..c7fa8a0fa
--- /dev/null
+++ b/lib/htmlpurifier/tests/HTMLPurifier/AttrDef/CSS/ImportantDecoratorTest.php
@@ -0,0 +1,49 @@
+mock = new HTMLPurifier_AttrDefMock();
+ $this->def = new HTMLPurifier_AttrDef_CSS_ImportantDecorator($this->mock, true);
+ }
+
+ protected function setMock($input, $output = null) {
+ if ($output === null) $output = $input;
+ $this->mock->expectOnce('validate', array($input, $this->config, $this->context));
+ $this->mock->setReturnValue('validate', $output);
+ }
+
+ function testImportant() {
+ $this->setMock('23');
+ $this->assertDef('23 !important');
+ }
+
+ function testImportantInternalDefChanged() {
+ $this->setMock('23', '24');
+ $this->assertDef('23 !important', '24 !important');
+ }
+
+ function testImportantWithSpace() {
+ $this->setMock('23');
+ $this->assertDef('23 ! important ', '23 !important');
+ }
+
+ function testFakeImportant() {
+ $this->setMock('! foo important');
+ $this->assertDef('! foo important');
+ }
+
+ function testStrip() {
+ $this->def = new HTMLPurifier_AttrDef_CSS_ImportantDecorator($this->mock, false);
+ $this->setMock('23');
+ $this->assertDef('23 ! important ', '23');
+ }
+
+}
+
+// vim: et sw=4 sts=4
diff --git a/lib/htmlpurifier/tests/HTMLPurifier/AttrDef/CSS/LengthTest.php b/lib/htmlpurifier/tests/HTMLPurifier/AttrDef/CSS/LengthTest.php
new file mode 100644
index 000000000..9d9fc41f2
--- /dev/null
+++ b/lib/htmlpurifier/tests/HTMLPurifier/AttrDef/CSS/LengthTest.php
@@ -0,0 +1,48 @@
+def = new HTMLPurifier_AttrDef_CSS_Length();
+
+ $this->assertDef('0');
+ $this->assertDef('0px');
+ $this->assertDef('4.5px');
+ $this->assertDef('-4.5px');
+ $this->assertDef('3ex');
+ $this->assertDef('3em');
+ $this->assertDef('3in');
+ $this->assertDef('3cm');
+ $this->assertDef('3mm');
+ $this->assertDef('3pt');
+ $this->assertDef('3pc');
+
+ $this->assertDef('3PX', '3px');
+
+ $this->assertDef('3', false);
+ $this->assertDef('3miles', false);
+
+ }
+
+ function testNonNegative() {
+
+ $this->def = new HTMLPurifier_AttrDef_CSS_Length('0');
+
+ $this->assertDef('3cm');
+ $this->assertDef('-3mm', false);
+
+ }
+
+ function testBounding() {
+ $this->def = new HTMLPurifier_AttrDef_CSS_Length('-1in', '1in');
+ $this->assertDef('1cm');
+ $this->assertDef('-1cm');
+ $this->assertDef('0');
+ $this->assertDef('1em', false);
+ }
+
+}
+
+// vim: et sw=4 sts=4
diff --git a/lib/htmlpurifier/tests/HTMLPurifier/AttrDef/CSS/ListStyleTest.php b/lib/htmlpurifier/tests/HTMLPurifier/AttrDef/CSS/ListStyleTest.php
new file mode 100644
index 000000000..070066705
--- /dev/null
+++ b/lib/htmlpurifier/tests/HTMLPurifier/AttrDef/CSS/ListStyleTest.php
@@ -0,0 +1,35 @@
+def = new HTMLPurifier_AttrDef_CSS_ListStyle($config);
+
+ $this->assertDef('lower-alpha');
+ $this->assertDef('upper-roman inside');
+ $this->assertDef('circle outside');
+ $this->assertDef('inside');
+ $this->assertDef('none');
+ $this->assertDef('url("foo.gif")');
+ $this->assertDef('circle url("foo.gif") inside');
+
+ // invalid values
+ $this->assertDef('outside inside', 'outside');
+
+ // ordering
+ $this->assertDef('url(foo.gif) none', 'none url("foo.gif")');
+ $this->assertDef('circle lower-alpha', 'circle');
+ // the spec is ambiguous about what happens in these
+ // cases, so we're going off the W3C CSS validator
+ $this->assertDef('disc none', 'disc');
+ $this->assertDef('none disc', 'none');
+
+
+ }
+
+}
+
+// vim: et sw=4 sts=4
diff --git a/lib/htmlpurifier/tests/HTMLPurifier/AttrDef/CSS/MultipleTest.php b/lib/htmlpurifier/tests/HTMLPurifier/AttrDef/CSS/MultipleTest.php
new file mode 100644
index 000000000..4461cb508
--- /dev/null
+++ b/lib/htmlpurifier/tests/HTMLPurifier/AttrDef/CSS/MultipleTest.php
@@ -0,0 +1,28 @@
+def = new HTMLPurifier_AttrDef_CSS_Multiple(
+ new HTMLPurifier_AttrDef_Integer()
+ );
+
+ $this->assertDef('1 2 3 4');
+ $this->assertDef('6');
+ $this->assertDef('4 5');
+ $this->assertDef(' 2 54 2 3', '2 54 2 3');
+ $this->assertDef("6\r3", '6 3');
+
+ $this->assertDef('asdf', false);
+ $this->assertDef('a s d f', false);
+ $this->assertDef('1 2 3 4 5', '1 2 3 4');
+ $this->assertDef('1 2 invalid 3', '1 2 3');
+
+
+ }
+
+}
+
+// vim: et sw=4 sts=4
diff --git a/lib/htmlpurifier/tests/HTMLPurifier/AttrDef/CSS/NumberTest.php b/lib/htmlpurifier/tests/HTMLPurifier/AttrDef/CSS/NumberTest.php
new file mode 100644
index 000000000..94e6ea8cf
--- /dev/null
+++ b/lib/htmlpurifier/tests/HTMLPurifier/AttrDef/CSS/NumberTest.php
@@ -0,0 +1,51 @@
+def = new HTMLPurifier_AttrDef_CSS_Number();
+
+ $this->assertDef('0');
+ $this->assertDef('0.0', '0');
+ $this->assertDef('1.0', '1');
+ $this->assertDef('34');
+ $this->assertDef('4.5');
+ $this->assertDef('.5');
+ $this->assertDef('0.5', '.5');
+ $this->assertDef('-56.9');
+
+ $this->assertDef('0.', '0');
+ $this->assertDef('.0', '0');
+ $this->assertDef('0.0', '0');
+
+ $this->assertDef('1.', '1');
+ $this->assertDef('.1', '.1');
+
+ $this->assertDef('1.0', '1');
+ $this->assertDef('0.1', '.1');
+
+ $this->assertDef('000', '0');
+ $this->assertDef(' 9', '9');
+ $this->assertDef('+5.0000', '5');
+ $this->assertDef('02.20', '2.2');
+ $this->assertDef('2.', '2');
+
+ $this->assertDef('.', false);
+ $this->assertDef('asdf', false);
+ $this->assertDef('0.5.6', false);
+
+ }
+
+ function testNonNegative() {
+
+ $this->def = new HTMLPurifier_AttrDef_CSS_Number(true);
+ $this->assertDef('23');
+ $this->assertDef('-12', false);
+
+ }
+
+}
+
+// vim: et sw=4 sts=4
diff --git a/lib/htmlpurifier/tests/HTMLPurifier/AttrDef/CSS/PercentageTest.php b/lib/htmlpurifier/tests/HTMLPurifier/AttrDef/CSS/PercentageTest.php
new file mode 100644
index 000000000..f712af1d2
--- /dev/null
+++ b/lib/htmlpurifier/tests/HTMLPurifier/AttrDef/CSS/PercentageTest.php
@@ -0,0 +1,24 @@
+def = new HTMLPurifier_AttrDef_CSS_Percentage();
+
+ $this->assertDef('10%');
+ $this->assertDef('1.607%');
+ $this->assertDef('-567%');
+
+ $this->assertDef(' 100% ', '100%');
+
+ $this->assertDef('5', false);
+ $this->assertDef('asdf', false);
+ $this->assertDef('%', false);
+
+ }
+
+}
+
+// vim: et sw=4 sts=4
diff --git a/lib/htmlpurifier/tests/HTMLPurifier/AttrDef/CSS/TextDecorationTest.php b/lib/htmlpurifier/tests/HTMLPurifier/AttrDef/CSS/TextDecorationTest.php
new file mode 100644
index 000000000..dd714d206
--- /dev/null
+++ b/lib/htmlpurifier/tests/HTMLPurifier/AttrDef/CSS/TextDecorationTest.php
@@ -0,0 +1,27 @@
+def = new HTMLPurifier_AttrDef_CSS_TextDecoration();
+
+ $this->assertDef('none');
+ $this->assertDef('none underline', 'underline');
+
+ $this->assertDef('underline');
+ $this->assertDef('overline');
+ $this->assertDef('line-through overline underline');
+ $this->assertDef('overline line-through');
+ $this->assertDef('UNDERLINE', 'underline');
+ $this->assertDef(' underline line-through ', 'underline line-through');
+
+ $this->assertDef('foobar underline', 'underline');
+ $this->assertDef('blink', false);
+
+ }
+
+}
+
+// vim: et sw=4 sts=4
diff --git a/lib/htmlpurifier/tests/HTMLPurifier/AttrDef/CSS/URITest.php b/lib/htmlpurifier/tests/HTMLPurifier/AttrDef/CSS/URITest.php
new file mode 100644
index 000000000..3d6f5791e
--- /dev/null
+++ b/lib/htmlpurifier/tests/HTMLPurifier/AttrDef/CSS/URITest.php
@@ -0,0 +1,29 @@
+def = new HTMLPurifier_AttrDef_CSS_URI();
+
+ $this->assertDef('', false);
+
+ // we could be nice but we won't be
+ $this->assertDef('http://www.example.com/', false);
+
+ $this->assertDef('url(', false);
+ $this->assertDef('url("")', true);
+ $result = 'url("http://www.example.com/")';
+ $this->assertDef('url(http://www.example.com/)', $result);
+ $this->assertDef('url("http://www.example.com/")', $result);
+ $this->assertDef("url('http://www.example.com/')", $result);
+ $this->assertDef(
+ ' url( "http://www.example.com/" ) ', $result);
+ $this->assertDef("url(http://www.example.com/foo,bar\)\'\()",
+ 'url("http://www.example.com/foo,bar%29%27%28")');
+ }
+
+}
+
+// vim: et sw=4 sts=4
diff --git a/lib/htmlpurifier/tests/HTMLPurifier/AttrDef/CSSTest.php b/lib/htmlpurifier/tests/HTMLPurifier/AttrDef/CSSTest.php
new file mode 100644
index 000000000..56917aece
--- /dev/null
+++ b/lib/htmlpurifier/tests/HTMLPurifier/AttrDef/CSSTest.php
@@ -0,0 +1,164 @@
+def = new HTMLPurifier_AttrDef_CSS();
+ }
+
+ function test() {
+
+ // regular cases, singular
+ $this->assertDef('text-align:right;');
+ $this->assertDef('border-left-style:solid;');
+ $this->assertDef('border-style:solid dotted;');
+ $this->assertDef('clear:right;');
+ $this->assertDef('float:left;');
+ $this->assertDef('font-style:italic;');
+ $this->assertDef('font-variant:small-caps;');
+ $this->assertDef('font-weight:bold;');
+ $this->assertDef('list-style-position:outside;');
+ $this->assertDef('list-style-type:upper-roman;');
+ $this->assertDef('list-style:upper-roman inside;');
+ $this->assertDef('text-transform:capitalize;');
+ $this->assertDef('background-color:rgb(0,0,255);');
+ $this->assertDef('background-color:transparent;');
+ $this->assertDef('background:#333 url("chess.png") repeat fixed 50% top;');
+ $this->assertDef('color:#F00;');
+ $this->assertDef('border-top-color:#F00;');
+ $this->assertDef('border-color:#F00 #FF0;');
+ $this->assertDef('border-top-width:thin;');
+ $this->assertDef('border-top-width:12px;');
+ $this->assertDef('border-width:5px 1px 4px 2px;');
+ $this->assertDef('border-top-width:-12px;', false);
+ $this->assertDef('letter-spacing:normal;');
+ $this->assertDef('letter-spacing:2px;');
+ $this->assertDef('word-spacing:normal;');
+ $this->assertDef('word-spacing:3em;');
+ $this->assertDef('font-size:200%;');
+ $this->assertDef('font-size:larger;');
+ $this->assertDef('font-size:12pt;');
+ $this->assertDef('line-height:2;');
+ $this->assertDef('line-height:2em;');
+ $this->assertDef('line-height:20%;');
+ $this->assertDef('line-height:normal;');
+ $this->assertDef('line-height:-20%;', false);
+ $this->assertDef('margin-left:5px;');
+ $this->assertDef('margin-right:20%;');
+ $this->assertDef('margin-top:auto;');
+ $this->assertDef('margin:auto 5%;');
+ $this->assertDef('padding-bottom:5px;');
+ $this->assertDef('padding-top:20%;');
+ $this->assertDef('padding:20% 10%;');
+ $this->assertDef('padding-top:-20%;', false);
+ $this->assertDef('text-indent:3em;');
+ $this->assertDef('text-indent:5%;');
+ $this->assertDef('text-indent:-3em;');
+ $this->assertDef('width:50%;');
+ $this->assertDef('width:50px;');
+ $this->assertDef('width:auto;');
+ $this->assertDef('width:-50px;', false);
+ $this->assertDef('text-decoration:underline;');
+ $this->assertDef('font-family:sans-serif;');
+ $this->assertDef("font-family:Gill, 'Times New Roman', sans-serif;");
+ $this->assertDef('font:12px serif;');
+ $this->assertDef('border:1px solid #000;');
+ $this->assertDef('border-bottom:2em double #FF00FA;');
+ $this->assertDef('border-collapse:collapse;');
+ $this->assertDef('border-collapse:separate;');
+ $this->assertDef('caption-side:top;');
+ $this->assertDef('vertical-align:middle;');
+ $this->assertDef('vertical-align:12px;');
+ $this->assertDef('vertical-align:50%;');
+ $this->assertDef('table-layout:fixed;');
+ $this->assertDef('list-style-image:url("nice.jpg");');
+ $this->assertDef('list-style:disc url("nice.jpg") inside;');
+ $this->assertDef('background-image:url("foo.jpg");');
+ $this->assertDef('background-image:none;');
+ $this->assertDef('background-repeat:repeat-y;');
+ $this->assertDef('background-attachment:fixed;');
+ $this->assertDef('background-position:left 90%;');
+ $this->assertDef('border-spacing:1em;');
+ $this->assertDef('border-spacing:1em 2em;');
+
+ // duplicates
+ $this->assertDef('text-align:right;text-align:left;',
+ 'text-align:left;');
+
+ // a few composites
+ $this->assertDef('font-variant:small-caps;font-weight:900;');
+ $this->assertDef('float:right;text-align:right;');
+
+ // selective removal
+ $this->assertDef('text-transform:capitalize;destroy:it;',
+ 'text-transform:capitalize;');
+
+ // inherit works for everything
+ $this->assertDef('text-align:inherit;');
+
+ // bad props
+ $this->assertDef('nodice:foobar;', false);
+ $this->assertDef('position:absolute;', false);
+ $this->assertDef('background-image:url(\'javascript:alert\(\)\');', false);
+
+ // airy input
+ $this->assertDef(' font-weight : bold; color : #ff0000',
+ 'font-weight:bold;color:#ff0000;');
+
+ // case-insensitivity
+ $this->assertDef('FLOAT:LEFT;', 'float:left;');
+
+ // !important stripping
+ $this->assertDef('float:left !important;', 'float:left;');
+
+ }
+
+ function testProprietary() {
+ $this->config->set('CSS.Proprietary', true);
+
+ $this->assertDef('scrollbar-arrow-color:#ff0;');
+ $this->assertDef('scrollbar-base-color:#ff6347;');
+ $this->assertDef('scrollbar-darkshadow-color:#ffa500;');
+ $this->assertDef('scrollbar-face-color:#008080;');
+ $this->assertDef('scrollbar-highlight-color:#ff69b4;');
+ $this->assertDef('scrollbar-shadow-color:#f0f;');
+
+ $this->assertDef('opacity:.2;');
+ $this->assertDef('-moz-opacity:.2;');
+ $this->assertDef('-khtml-opacity:.2;');
+ $this->assertDef('filter:alpha(opacity=20);');
+
+ }
+
+ function testImportant() {
+ $this->config->set('CSS.AllowImportant', true);
+ $this->assertDef('float:left !important;');
+ }
+
+ function testTricky() {
+ $this->config->set('CSS.AllowTricky', true);
+ $this->assertDef('display:none;');
+ $this->assertDef('visibility:visible;');
+ $this->assertDef('overflow:scroll;');
+ }
+
+ function testForbidden() {
+ $this->config->set('CSS.ForbiddenProperties', 'float');
+ $this->assertDef('float:left;', false);
+ $this->assertDef('text-align:right;');
+ }
+
+ function testTrusted() {
+ $this->config->set('CSS.Trusted', true);
+ $this->assertDef('position:relative;');
+ $this->assertDef('left:2px;');
+ $this->assertDef('right:100%;');
+ $this->assertDef('top:auto;');
+ $this->assertDef('z-index:-2;');
+ }
+
+}
+
+// vim: et sw=4 sts=4
diff --git a/lib/htmlpurifier/tests/HTMLPurifier/AttrDef/EnumTest.php b/lib/htmlpurifier/tests/HTMLPurifier/AttrDef/EnumTest.php
new file mode 100644
index 000000000..7722c1bc2
--- /dev/null
+++ b/lib/htmlpurifier/tests/HTMLPurifier/AttrDef/EnumTest.php
@@ -0,0 +1,37 @@
+def = new HTMLPurifier_AttrDef_Enum(array('one', 'two'));
+ $this->assertDef('one');
+ $this->assertDef('ONE', 'one');
+ }
+
+ function testCaseSensitive() {
+ $this->def = new HTMLPurifier_AttrDef_Enum(array('one', 'two'), true);
+ $this->assertDef('one');
+ $this->assertDef('ONE', false);
+ }
+
+ function testFixing() {
+ $this->def = new HTMLPurifier_AttrDef_Enum(array('one'));
+ $this->assertDef(' one ', 'one');
+ }
+
+ function test_make() {
+ $factory = new HTMLPurifier_AttrDef_Enum();
+
+ $def = $factory->make('foo,bar');
+ $def2 = new HTMLPurifier_AttrDef_Enum(array('foo', 'bar'));
+ $this->assertIdentical($def, $def2);
+
+ $def = $factory->make('s:foo,BAR');
+ $def2 = new HTMLPurifier_AttrDef_Enum(array('foo', 'BAR'), true);
+ $this->assertIdentical($def, $def2);
+ }
+
+}
+
+// vim: et sw=4 sts=4
diff --git a/lib/htmlpurifier/tests/HTMLPurifier/AttrDef/HTML/BoolTest.php b/lib/htmlpurifier/tests/HTMLPurifier/AttrDef/HTML/BoolTest.php
new file mode 100644
index 000000000..060d0fc8f
--- /dev/null
+++ b/lib/htmlpurifier/tests/HTMLPurifier/AttrDef/HTML/BoolTest.php
@@ -0,0 +1,22 @@
+def = new HTMLPurifier_AttrDef_HTML_Bool('foo');
+ $this->assertDef('foo');
+ $this->assertDef('', false);
+ $this->assertDef('bar', 'foo');
+ }
+
+ function test_make() {
+ $factory = new HTMLPurifier_AttrDef_HTML_Bool();
+ $def = $factory->make('foo');
+ $def2 = new HTMLPurifier_AttrDef_HTML_Bool('foo');
+ $this->assertIdentical($def, $def2);
+ }
+
+}
+
+// vim: et sw=4 sts=4
diff --git a/lib/htmlpurifier/tests/HTMLPurifier/AttrDef/HTML/ClassTest.php b/lib/htmlpurifier/tests/HTMLPurifier/AttrDef/HTML/ClassTest.php
new file mode 100644
index 000000000..6effd3cde
--- /dev/null
+++ b/lib/htmlpurifier/tests/HTMLPurifier/AttrDef/HTML/ClassTest.php
@@ -0,0 +1,48 @@
+def = new HTMLPurifier_AttrDef_HTML_Class();
+ }
+ function testAllowedClasses() {
+ $this->config->set('Attr.AllowedClasses', array('foo'));
+ $this->assertDef('foo');
+ $this->assertDef('bar', false);
+ $this->assertDef('foo bar', 'foo');
+ }
+ function testForbiddenClasses() {
+ $this->config->set('Attr.ForbiddenClasses', array('bar'));
+ $this->assertDef('foo');
+ $this->assertDef('bar', false);
+ $this->assertDef('foo bar', 'foo');
+ }
+ function testDefault() {
+ $this->assertDef('valid');
+ $this->assertDef('a0-_');
+ $this->assertDef('-valid');
+ $this->assertDef('_valid');
+ $this->assertDef('double valid');
+
+ $this->assertDef('0stillvalid');
+ $this->assertDef('-0');
+
+ // test conditional replacement
+ $this->assertDef('validassoc 0valid', 'validassoc 0valid');
+
+ // test whitespace leniency
+ $this->assertDef(" double\nvalid\r", 'double valid');
+
+ // test case sensitivity
+ $this->assertDef('VALID');
+
+ // test duplicate removal
+ $this->assertDef('valid valid', 'valid');
+ }
+ function testXHTML11Behavior() {
+ $this->config->set('HTML.Doctype', 'XHTML 1.1');
+ $this->assertDef('0invalid', false);
+ $this->assertDef('valid valid', 'valid');
+ }
+}
diff --git a/lib/htmlpurifier/tests/HTMLPurifier/AttrDef/HTML/ColorTest.php b/lib/htmlpurifier/tests/HTMLPurifier/AttrDef/HTML/ColorTest.php
new file mode 100644
index 000000000..8b4a46347
--- /dev/null
+++ b/lib/htmlpurifier/tests/HTMLPurifier/AttrDef/HTML/ColorTest.php
@@ -0,0 +1,20 @@
+def = new HTMLPurifier_AttrDef_HTML_Color();
+ $this->assertDef('', false);
+ $this->assertDef('foo', false);
+ $this->assertDef('43', false);
+ $this->assertDef('red', '#FF0000');
+ $this->assertDef('#FF0000');
+ $this->assertDef('#453443');
+ $this->assertDef('453443', '#453443');
+ $this->assertDef('#345', '#334455');
+ $this->assertDef('120', '#112200');
+ }
+}
+
+// vim: et sw=4 sts=4
diff --git a/lib/htmlpurifier/tests/HTMLPurifier/AttrDef/HTML/FrameTargetTest.php b/lib/htmlpurifier/tests/HTMLPurifier/AttrDef/HTML/FrameTargetTest.php
new file mode 100644
index 000000000..7d3e24c75
--- /dev/null
+++ b/lib/htmlpurifier/tests/HTMLPurifier/AttrDef/HTML/FrameTargetTest.php
@@ -0,0 +1,28 @@
+def = new HTMLPurifier_AttrDef_HTML_FrameTarget();
+ }
+
+ function testNoneAllowed() {
+ $this->assertDef('', false);
+ $this->assertDef('foo', false);
+ $this->assertDef('_blank', false);
+ $this->assertDef('baz', false);
+ }
+
+ function test() {
+ $this->config->set('Attr.AllowedFrameTargets', 'foo,_blank');
+ $this->assertDef('', false);
+ $this->assertDef('foo');
+ $this->assertDef('_blank');
+ $this->assertDef('baz', false);
+ }
+
+}
+
+// vim: et sw=4 sts=4
diff --git a/lib/htmlpurifier/tests/HTMLPurifier/AttrDef/HTML/IDTest.php b/lib/htmlpurifier/tests/HTMLPurifier/AttrDef/HTML/IDTest.php
new file mode 100644
index 000000000..245db16da
--- /dev/null
+++ b/lib/htmlpurifier/tests/HTMLPurifier/AttrDef/HTML/IDTest.php
@@ -0,0 +1,108 @@
+context->register('IDAccumulator', $id_accumulator);
+ $this->config->set('Attr.EnableID', true);
+ $this->def = new HTMLPurifier_AttrDef_HTML_ID();
+
+ }
+
+ function test() {
+
+ // valid ID names
+ $this->assertDef('alpha');
+ $this->assertDef('al_ha');
+ $this->assertDef('a0-:.');
+ $this->assertDef('a');
+
+ // invalid ID names
+ $this->assertDef('assertDef('0123', false);
+ $this->assertDef('.asa', false);
+
+ // test duplicate detection
+ $this->assertDef('once');
+ $this->assertDef('once', false);
+
+ // valid once whitespace stripped, but needs to be amended
+ $this->assertDef(' whee ', 'whee');
+
+ }
+
+ function testPrefix() {
+
+ $this->config->set('Attr.IDPrefix', 'user_');
+
+ $this->assertDef('alpha', 'user_alpha');
+ $this->assertDef('assertDef('once', 'user_once');
+ $this->assertDef('once', false);
+
+ // if already prefixed, leave alone
+ $this->assertDef('user_alas');
+ $this->assertDef('user_user_alas'); // how to bypass
+
+ }
+
+ function testTwoPrefixes() {
+
+ $this->config->set('Attr.IDPrefix', 'user_');
+ $this->config->set('Attr.IDPrefixLocal', 'story95_');
+
+ $this->assertDef('alpha', 'user_story95_alpha');
+ $this->assertDef('assertDef('once', 'user_story95_once');
+ $this->assertDef('once', false);
+
+ $this->assertDef('user_story95_alas');
+ $this->assertDef('user_alas', 'user_story95_user_alas'); // !
+ }
+
+ function testLocalPrefixWithoutMainPrefix() {
+ // no effect when IDPrefix isn't set
+ $this->config->set('Attr.IDPrefix', '');
+ $this->config->set('Attr.IDPrefixLocal', 'story95_');
+ $this->expectError('%Attr.IDPrefixLocal cannot be used unless '.
+ '%Attr.IDPrefix is set');
+ $this->assertDef('amherst');
+
+ }
+
+ // reference functionality is disabled for now
+ function disabled_testIDReference() {
+
+ $this->def = new HTMLPurifier_AttrDef_HTML_ID(true);
+
+ $this->assertDef('good_id');
+ $this->assertDef('good_id'); // duplicates okay
+ $this->assertDef('', false);
+
+ $this->def = new HTMLPurifier_AttrDef_HTML_ID();
+
+ $this->assertDef('good_id');
+ $this->assertDef('good_id', false); // duplicate now not okay
+
+ $this->def = new HTMLPurifier_AttrDef_HTML_ID(true);
+
+ $this->assertDef('good_id'); // reference still okay
+
+ }
+
+ function testRegexp() {
+
+ $this->config->set('Attr.IDBlacklistRegexp', '/^g_/');
+
+ $this->assertDef('good_id');
+ $this->assertDef('g_bad_id', false);
+
+ }
+
+}
+
+// vim: et sw=4 sts=4
diff --git a/lib/htmlpurifier/tests/HTMLPurifier/AttrDef/HTML/LengthTest.php b/lib/htmlpurifier/tests/HTMLPurifier/AttrDef/HTML/LengthTest.php
new file mode 100644
index 000000000..d165e30b5
--- /dev/null
+++ b/lib/htmlpurifier/tests/HTMLPurifier/AttrDef/HTML/LengthTest.php
@@ -0,0 +1,32 @@
+def = new HTMLPurifier_AttrDef_HTML_Length();
+ }
+
+ function test() {
+
+ // pixel check
+ parent::test();
+
+ // percent check
+ $this->assertDef('25%');
+
+ // Firefox maintains percent, so will we
+ $this->assertDef('0%');
+
+ // 0% <= percent <= 100%
+ $this->assertDef('-15%', '0%');
+ $this->assertDef('120%', '100%');
+
+ // fractional percents, apparently, aren't allowed
+ $this->assertDef('56.5%', '56%');
+
+ }
+
+}
+
+// vim: et sw=4 sts=4
diff --git a/lib/htmlpurifier/tests/HTMLPurifier/AttrDef/HTML/LinkTypesTest.php b/lib/htmlpurifier/tests/HTMLPurifier/AttrDef/HTML/LinkTypesTest.php
new file mode 100644
index 000000000..d90b65b1f
--- /dev/null
+++ b/lib/htmlpurifier/tests/HTMLPurifier/AttrDef/HTML/LinkTypesTest.php
@@ -0,0 +1,21 @@
+def = new HTMLPurifier_AttrDef_HTML_LinkTypes('rel');
+ $this->config->set('Attr.AllowedRel', array('nofollow', 'foo'));
+
+ $this->assertDef('', false);
+ $this->assertDef('nofollow', true);
+ $this->assertDef('nofollow foo', true);
+ $this->assertDef('nofollow bar', 'nofollow');
+ $this->assertDef('bar', false);
+
+ }
+
+}
+
+// vim: et sw=4 sts=4
diff --git a/lib/htmlpurifier/tests/HTMLPurifier/AttrDef/HTML/MultiLengthTest.php b/lib/htmlpurifier/tests/HTMLPurifier/AttrDef/HTML/MultiLengthTest.php
new file mode 100644
index 000000000..eb6f34011
--- /dev/null
+++ b/lib/htmlpurifier/tests/HTMLPurifier/AttrDef/HTML/MultiLengthTest.php
@@ -0,0 +1,28 @@
+def = new HTMLPurifier_AttrDef_HTML_MultiLength();
+ }
+
+ function test() {
+
+ // length check
+ parent::test();
+
+ $this->assertDef('*');
+ $this->assertDef('1*', '*');
+ $this->assertDef('56*');
+
+ $this->assertDef('**', false); // plain old bad
+
+ $this->assertDef('5.4*', '5*'); // no decimals
+ $this->assertDef('-3*', false); // no negatives
+
+ }
+
+}
+
+// vim: et sw=4 sts=4
diff --git a/lib/htmlpurifier/tests/HTMLPurifier/AttrDef/HTML/NmtokensTest.php b/lib/htmlpurifier/tests/HTMLPurifier/AttrDef/HTML/NmtokensTest.php
new file mode 100644
index 000000000..bb64ff6e2
--- /dev/null
+++ b/lib/htmlpurifier/tests/HTMLPurifier/AttrDef/HTML/NmtokensTest.php
@@ -0,0 +1,35 @@
+def = new HTMLPurifier_AttrDef_HTML_Nmtokens();
+ }
+
+ function testDefault() {
+
+ $this->assertDef('valid');
+ $this->assertDef('a0-_');
+ $this->assertDef('-valid');
+ $this->assertDef('_valid');
+ $this->assertDef('double valid');
+
+ $this->assertDef('0invalid', false);
+ $this->assertDef('-0', false);
+
+ // test conditional replacement
+ $this->assertDef('validassoc 0invalid', 'validassoc');
+
+ // test whitespace leniency
+ $this->assertDef(" double\nvalid\r", 'double valid');
+
+ // test case sensitivity
+ $this->assertDef('VALID');
+
+ }
+
+}
+
+// vim: et sw=4 sts=4
diff --git a/lib/htmlpurifier/tests/HTMLPurifier/AttrDef/HTML/PixelsTest.php b/lib/htmlpurifier/tests/HTMLPurifier/AttrDef/HTML/PixelsTest.php
new file mode 100644
index 000000000..08f25be64
--- /dev/null
+++ b/lib/htmlpurifier/tests/HTMLPurifier/AttrDef/HTML/PixelsTest.php
@@ -0,0 +1,45 @@
+def = new HTMLPurifier_AttrDef_HTML_Pixels();
+ }
+
+ function test() {
+
+ $this->assertDef('1');
+ $this->assertDef('0');
+
+ $this->assertDef('2px', '2'); // rm px suffix
+
+ $this->assertDef('dfs', false); // totally invalid value
+
+ // conceivably we could repair this value, but we won't for now
+ $this->assertDef('9in', false);
+
+ // test trim
+ $this->assertDef(' 45 ', '45');
+
+ // no negatives
+ $this->assertDef('-2', '0');
+
+ // remove empty
+ $this->assertDef('', false);
+
+ // round down
+ $this->assertDef('4.9', '4');
+
+ }
+
+ function test_make() {
+ $factory = new HTMLPurifier_AttrDef_HTML_Pixels();
+ $this->def = $factory->make('30');
+ $this->assertDef('25');
+ $this->assertDef('35', '30');
+ }
+
+}
+
+// vim: et sw=4 sts=4
diff --git a/lib/htmlpurifier/tests/HTMLPurifier/AttrDef/IntegerTest.php b/lib/htmlpurifier/tests/HTMLPurifier/AttrDef/IntegerTest.php
new file mode 100644
index 000000000..a941e31ab
--- /dev/null
+++ b/lib/htmlpurifier/tests/HTMLPurifier/AttrDef/IntegerTest.php
@@ -0,0 +1,61 @@
+def = new HTMLPurifier_AttrDef_Integer();
+
+ $this->assertDef('0');
+ $this->assertDef('1');
+ $this->assertDef('-1');
+ $this->assertDef('-10');
+ $this->assertDef('14');
+ $this->assertDef('+24', '24');
+ $this->assertDef(' 14 ', '14');
+ $this->assertDef('-0', '0');
+
+ $this->assertDef('-1.4', false);
+ $this->assertDef('3.4', false);
+ $this->assertDef('asdf', false); // must not return zero
+ $this->assertDef('2in', false); // must not return zero
+
+ }
+
+ function assertRange($negative, $zero, $positive) {
+ $this->assertDef('-100', $negative);
+ $this->assertDef('-1', $negative);
+ $this->assertDef('0', $zero);
+ $this->assertDef('1', $positive);
+ $this->assertDef('42', $positive);
+ }
+
+ function testRange() {
+
+ $this->def = new HTMLPurifier_AttrDef_Integer(false);
+ $this->assertRange(false, true, true); // non-negative
+
+ $this->def = new HTMLPurifier_AttrDef_Integer(false, false);
+ $this->assertRange(false, false, true); // positive
+
+
+ // fringe cases
+
+ $this->def = new HTMLPurifier_AttrDef_Integer(false, false, false);
+ $this->assertRange(false, false, false); // allow none
+
+ $this->def = new HTMLPurifier_AttrDef_Integer(true, false, false);
+ $this->assertRange(true, false, false); // negative
+
+ $this->def = new HTMLPurifier_AttrDef_Integer(false, true, false);
+ $this->assertRange(false, true, false); // zero
+
+ $this->def = new HTMLPurifier_AttrDef_Integer(true, true, false);
+ $this->assertRange(true, true, false); // non-positive
+
+ }
+
+}
+
+// vim: et sw=4 sts=4
diff --git a/lib/htmlpurifier/tests/HTMLPurifier/AttrDef/LangTest.php b/lib/htmlpurifier/tests/HTMLPurifier/AttrDef/LangTest.php
new file mode 100644
index 000000000..c59175556
--- /dev/null
+++ b/lib/htmlpurifier/tests/HTMLPurifier/AttrDef/LangTest.php
@@ -0,0 +1,85 @@
+def = new HTMLPurifier_AttrDef_Lang();
+
+ // basic good uses
+ $this->assertDef('en');
+ $this->assertDef('en-us');
+
+ $this->assertDef(' en ', 'en'); // trim
+ $this->assertDef('EN', 'en'); // case insensitivity
+
+ // (thanks Eugen Pankratz for noticing the typos!)
+ $this->assertDef('En-Us-Edison', 'en-us-edison'); // complex ci
+
+ $this->assertDef('fr en', false); // multiple languages
+ $this->assertDef('%', false); // bad character
+
+ // test overlong language according to syntax
+ $this->assertDef('thisistoolongsoitgetscut', false);
+
+ // primary subtag rules
+ // I'm somewhat hesitant to allow x and i as primary language codes,
+ // because they usually are never used in real life. However,
+ // theoretically speaking, having them alone is permissable, so
+ // I'll be lenient. No XML parser is going to complain anyway.
+ $this->assertDef('x');
+ $this->assertDef('i');
+ // real world use-cases
+ $this->assertDef('x-klingon');
+ $this->assertDef('i-mingo');
+ // because the RFC only defines two and three letter primary codes,
+ // anything with a length of four or greater is invalid, despite
+ // the syntax stipulation of 1 to 8 characters. Because the RFC
+ // specifically states that this reservation is in order to allow
+ // for future versions to expand, the adoption of a new RFC will
+ // require these test cases to be rewritten, even if backwards-
+ // compatibility is largely retained (i.e. this is not forwards
+ // compatible)
+ $this->assertDef('four', false);
+ // for similar reasons, disallow any other one character language
+ $this->assertDef('f', false);
+
+ // second subtag rules
+ // one letter subtags prohibited until revision. This is, however,
+ // less volatile than the restrictions on the primary subtags.
+ // Also note that this test-case tests fix-behavior: chop
+ // off subtags until you get a valid language code.
+ $this->assertDef('en-a', 'en');
+ // however, x is a reserved single-letter subtag that is allowed
+ $this->assertDef('en-x', 'en-x');
+ // 2-8 chars are permitted, but have special meaning that cannot
+ // be checked without maintaining country code lookup tables (for
+ // two characters) or special registration tables (for all above).
+ $this->assertDef('en-uk', true);
+
+ // further subtag rules: only syntactic constraints
+ $this->assertDef('en-us-edison');
+ $this->assertDef('en-us-toolonghaha', 'en-us');
+ $this->assertDef('en-us-a-silly-long-one');
+
+ // rfc 3066 stipulates that if a three letter and a two letter code
+ // are available, the two letter one MUST be used. Without a language
+ // code lookup table, we cannot implement this functionality.
+
+ // although the HTML protocol, technically speaking, allows you to
+ // omit language tags, this implicitly means that the parent element's
+ // language is the one applicable, which, in some cases, is incorrect.
+ // Thus, we allow und, only slightly defying the RFC's SHOULD NOT
+ // designation.
+ $this->assertDef('und');
+
+ // because attributes only allow one language, mul is allowed, complying
+ // with the RFC's SHOULD NOT designation.
+ $this->assertDef('mul');
+
+ }
+
+}
+
+// vim: et sw=4 sts=4
diff --git a/lib/htmlpurifier/tests/HTMLPurifier/AttrDef/SwitchTest.php b/lib/htmlpurifier/tests/HTMLPurifier/AttrDef/SwitchTest.php
new file mode 100644
index 000000000..21bafe697
--- /dev/null
+++ b/lib/htmlpurifier/tests/HTMLPurifier/AttrDef/SwitchTest.php
@@ -0,0 +1,34 @@
+with = new HTMLPurifier_AttrDefMock();
+ $this->without = new HTMLPurifier_AttrDefMock();
+ $this->def = new HTMLPurifier_AttrDef_Switch('tag', $this->with, $this->without);
+ }
+
+ function testWith() {
+ $token = new HTMLPurifier_Token_Start('tag');
+ $this->context->register('CurrentToken', $token);
+ $this->with->expectOnce('validate');
+ $this->with->setReturnValue('validate', 'foo');
+ $this->assertDef('bar', 'foo');
+ }
+
+ function testWithout() {
+ $token = new HTMLPurifier_Token_Start('other-tag');
+ $this->context->register('CurrentToken', $token);
+ $this->without->expectOnce('validate');
+ $this->without->setReturnValue('validate', 'foo');
+ $this->assertDef('bar', 'foo');
+ }
+
+}
+
+// vim: et sw=4 sts=4
diff --git a/lib/htmlpurifier/tests/HTMLPurifier/AttrDef/TextTest.php b/lib/htmlpurifier/tests/HTMLPurifier/AttrDef/TextTest.php
new file mode 100644
index 000000000..458008aa8
--- /dev/null
+++ b/lib/htmlpurifier/tests/HTMLPurifier/AttrDef/TextTest.php
@@ -0,0 +1,17 @@
+def = new HTMLPurifier_AttrDef_Text();
+
+ $this->assertDef('This is spiffy text!');
+ $this->assertDef(" Casual\tCDATA parse\ncheck. ", 'Casual CDATA parse check.');
+
+ }
+
+}
+
+// vim: et sw=4 sts=4
diff --git a/lib/htmlpurifier/tests/HTMLPurifier/AttrDef/URI/Email/SimpleCheckTest.php b/lib/htmlpurifier/tests/HTMLPurifier/AttrDef/URI/Email/SimpleCheckTest.php
new file mode 100644
index 000000000..c310347e9
--- /dev/null
+++ b/lib/htmlpurifier/tests/HTMLPurifier/AttrDef/URI/Email/SimpleCheckTest.php
@@ -0,0 +1,13 @@
+def = new HTMLPurifier_AttrDef_URI_Email_SimpleCheck();
+ }
+
+}
+
+// vim: et sw=4 sts=4
diff --git a/lib/htmlpurifier/tests/HTMLPurifier/AttrDef/URI/EmailHarness.php b/lib/htmlpurifier/tests/HTMLPurifier/AttrDef/URI/EmailHarness.php
new file mode 100644
index 000000000..594e2ce29
--- /dev/null
+++ b/lib/htmlpurifier/tests/HTMLPurifier/AttrDef/URI/EmailHarness.php
@@ -0,0 +1,31 @@
+assertDef('bob@example.com');
+ $this->assertDef(' bob@example.com ', 'bob@example.com');
+ $this->assertDef('bob.thebuilder@example.net');
+ $this->assertDef('Bob_the_Builder-the-2nd@example.org');
+ $this->assertDef('Bob%20the%20Builder@white-space.test');
+
+ // extended format, with real name
+ //$this->assertDef('Bob%20Builder%20%3Cbobby.bob.bob@it.is.example.com%3E');
+ //$this->assertDef('Bob Builder ');
+
+ // time to fail
+ $this->assertDef('bob', false);
+ $this->assertDef('bob@home@work', false);
+ $this->assertDef('@example.com', false);
+ $this->assertDef('bob@', false);
+ $this->assertDef('', false);
+
+ }
+
+}
+
+// vim: et sw=4 sts=4
diff --git a/lib/htmlpurifier/tests/HTMLPurifier/AttrDef/URI/HostTest.php b/lib/htmlpurifier/tests/HTMLPurifier/AttrDef/URI/HostTest.php
new file mode 100644
index 000000000..b5827718b
--- /dev/null
+++ b/lib/htmlpurifier/tests/HTMLPurifier/AttrDef/URI/HostTest.php
@@ -0,0 +1,53 @@
+def = new HTMLPurifier_AttrDef_URI_Host();
+
+ $this->assertDef('[2001:DB8:0:0:8:800:200C:417A]'); // IPv6
+ $this->assertDef('124.15.6.89'); // IPv4
+ $this->assertDef('www.google.com'); // reg-name
+
+ // more domain name tests
+ $this->assertDef('test.');
+ $this->assertDef('sub.test.');
+ $this->assertDef('.test', false);
+ $this->assertDef('ff');
+ $this->assertDef('1f', false);
+ $this->assertDef('-f', false);
+ $this->assertDef('f1');
+ $this->assertDef('f-', false);
+ $this->assertDef('sub.ff');
+ $this->assertDef('sub.1f', false);
+ $this->assertDef('sub.-f', false);
+ $this->assertDef('sub.f1');
+ $this->assertDef('sub.f-', false);
+ $this->assertDef('ff.top');
+ $this->assertDef('1f.top');
+ $this->assertDef('-f.top', false);
+ $this->assertDef('ff.top');
+ $this->assertDef('f1.top');
+ $this->assertDef('f-.top', false);
+
+ $this->assertDef("\xE4\xB8\xAD\xE6\x96\x87.com.cn", false);
+
+ }
+
+ function testIDNA() {
+ if (!$GLOBALS['HTMLPurifierTest']['Net_IDNA2']) {
+ return false;
+ }
+ $this->config->set('Core.EnableIDNA', true);
+ $this->assertDef("\xE4\xB8\xAD\xE6\x96\x87.com.cn", "xn--fiq228c.com.cn");
+ $this->assertDef("\xe2\x80\x85.com", false); // rejected
+ }
+
+}
+
+// vim: et sw=4 sts=4
diff --git a/lib/htmlpurifier/tests/HTMLPurifier/AttrDef/URI/IPv4Test.php b/lib/htmlpurifier/tests/HTMLPurifier/AttrDef/URI/IPv4Test.php
new file mode 100644
index 000000000..0a4eb17ba
--- /dev/null
+++ b/lib/htmlpurifier/tests/HTMLPurifier/AttrDef/URI/IPv4Test.php
@@ -0,0 +1,25 @@
+def = new HTMLPurifier_AttrDef_URI_IPv4();
+
+ $this->assertDef('127.0.0.1'); // standard IPv4, loopback, non-routable
+ $this->assertDef('0.0.0.0'); // standard IPv4, unspecified, non-routable
+ $this->assertDef('255.255.255.255'); // standard IPv4
+
+ $this->assertDef('300.0.0.0', false); // standard IPv4, out of range
+ $this->assertDef('124.15.6.89/60', false); // standard IPv4, prefix not allowed
+
+ $this->assertDef('', false); // nothing
+
+ }
+}
+
+// vim: et sw=4 sts=4
diff --git a/lib/htmlpurifier/tests/HTMLPurifier/AttrDef/URI/IPv6Test.php b/lib/htmlpurifier/tests/HTMLPurifier/AttrDef/URI/IPv6Test.php
new file mode 100644
index 000000000..083e818aa
--- /dev/null
+++ b/lib/htmlpurifier/tests/HTMLPurifier/AttrDef/URI/IPv6Test.php
@@ -0,0 +1,43 @@
+def = new HTMLPurifier_AttrDef_URI_IPv6();
+
+ $this->assertDef('2001:DB8:0:0:8:800:200C:417A'); // unicast, full
+ $this->assertDef('FF01:0:0:0:0:0:0:101'); // multicast, full
+ $this->assertDef('0:0:0:0:0:0:0:1'); // loopback, full
+ $this->assertDef('0:0:0:0:0:0:0:0'); // unspecified, full
+ $this->assertDef('2001:DB8::8:800:200C:417A'); // unicast, compressed
+ $this->assertDef('FF01::101'); // multicast, compressed
+
+ $this->assertDef('::1'); // loopback, compressed, non-routable
+ $this->assertDef('::'); // unspecified, compressed, non-routable
+ $this->assertDef('0:0:0:0:0:0:13.1.68.3'); // IPv4-compatible IPv6 address, full, deprecated
+ $this->assertDef('0:0:0:0:0:FFFF:129.144.52.38'); // IPv4-mapped IPv6 address, full
+ $this->assertDef('::13.1.68.3'); // IPv4-compatible IPv6 address, compressed, deprecated
+ $this->assertDef('::FFFF:129.144.52.38'); // IPv4-mapped IPv6 address, compressed
+ $this->assertDef('2001:0DB8:0000:CD30:0000:0000:0000:0000/60'); // full, with prefix
+ $this->assertDef('2001:0DB8::CD30:0:0:0:0/60'); // compressed, with prefix
+ $this->assertDef('2001:0DB8:0:CD30::/60'); // compressed, with prefix #2
+ $this->assertDef('::/128'); // compressed, unspecified address type, non-routable
+ $this->assertDef('::1/128'); // compressed, loopback address type, non-routable
+ $this->assertDef('FF00::/8'); // compressed, multicast address type
+ $this->assertDef('FE80::/10'); // compressed, link-local unicast, non-routable
+ $this->assertDef('FEC0::/10'); // compressed, site-local unicast, deprecated
+
+ $this->assertDef('2001:DB8:0:0:8:800:200C:417A:221', false); // unicast, full
+ $this->assertDef('FF01::101::2', false); //multicast, compressed
+ $this->assertDef('', false); // nothing
+
+ }
+
+}
+
+// vim: et sw=4 sts=4
diff --git a/lib/htmlpurifier/tests/HTMLPurifier/AttrDef/URITest.php b/lib/htmlpurifier/tests/HTMLPurifier/AttrDef/URITest.php
new file mode 100644
index 000000000..3044367a2
--- /dev/null
+++ b/lib/htmlpurifier/tests/HTMLPurifier/AttrDef/URITest.php
@@ -0,0 +1,146 @@
+def = new HTMLPurifier_AttrDef_URI();
+ parent::setUp();
+ }
+
+ function testIntegration() {
+ $this->assertDef('http://www.google.com/');
+ $this->assertDef('http:', '');
+ $this->assertDef('http:/foo', '/foo');
+ $this->assertDef('javascript:bad_stuff();', false);
+ $this->assertDef('ftp://www.example.com/');
+ $this->assertDef('news:rec.alt');
+ $this->assertDef('nntp://news.example.com/324234');
+ $this->assertDef('mailto:bob@example.com');
+ }
+
+ function testIntegrationWithPercentEncoder() {
+ $this->assertDef(
+ 'http://www.example.com/%56%fc%GJ%5%FC',
+ 'http://www.example.com/V%FC%25GJ%255%FC'
+ );
+ }
+
+ function testPercentEncoding() {
+ $this->assertDef(
+ 'http:colon:mercenary',
+ 'colon%3Amercenary'
+ );
+ }
+
+ function testPercentEncodingPreserve() {
+ $this->assertDef(
+ 'http://www.example.com/abcABC123-_.!~*()\''
+ );
+ }
+
+ function testEmbeds() {
+ $this->def = new HTMLPurifier_AttrDef_URI(true);
+ $this->assertDef('http://sub.example.com/alas?foo=asd');
+ $this->assertDef('mailto:foo@example.com', false);
+ }
+
+ function testConfigMunge() {
+ $this->config->set('URI.Munge', 'http://www.google.com/url?q=%s');
+ $this->assertDef(
+ 'http://www.example.com/',
+ 'http://www.google.com/url?q=http%3A%2F%2Fwww.example.com%2F'
+ );
+ $this->assertDef('index.html');
+ $this->assertDef('javascript:foobar();', false);
+ }
+
+ function testDefaultSchemeRemovedInBlank() {
+ $this->assertDef('http:', '');
+ }
+
+ function testDefaultSchemeRemovedInRelativeURI() {
+ $this->assertDef('http:/foo/bar', '/foo/bar');
+ }
+
+ function testDefaultSchemeNotRemovedInAbsoluteURI() {
+ $this->assertDef('http://example.com/foo/bar');
+ }
+
+ function testAltSchemeNotRemoved() {
+ $this->assertDef('mailto:this-looks-like-a-path@example.com');
+ }
+
+ function testResolveNullSchemeAmbiguity() {
+ $this->assertDef('///foo', '/foo');
+ }
+
+ function testResolveNullSchemeDoubleAmbiguity() {
+ $this->config->set('URI.Host', 'example.com');
+ $this->assertDef('////foo', '//example.com//foo');
+ }
+
+ function testURIDefinitionValidation() {
+ $parser = new HTMLPurifier_URIParser();
+ $uri = $parser->parse('http://example.com');
+ $this->config->set('URI.DefinitionID', 'HTMLPurifier_AttrDef_URITest->testURIDefinitionValidation');
+
+ generate_mock_once('HTMLPurifier_URIDefinition');
+ $uri_def = new HTMLPurifier_URIDefinitionMock();
+ $uri_def->expectOnce('filter', array($uri, '*', '*'));
+ $uri_def->setReturnValue('filter', true, array($uri, '*', '*'));
+ $uri_def->expectOnce('postFilter', array($uri, '*', '*'));
+ $uri_def->setReturnValue('postFilter', true, array($uri, '*', '*'));
+ $uri_def->setup = true;
+
+ // Since definitions are no longer passed by reference, we need
+ // to muck around with the cache to insert our mock. This is
+ // technically a little bad, since the cache shouldn't change
+ // behavior, but I don't feel too good about letting users
+ // overload entire definitions.
+ generate_mock_once('HTMLPurifier_DefinitionCache');
+ $cache_mock = new HTMLPurifier_DefinitionCacheMock();
+ $cache_mock->setReturnValue('get', $uri_def);
+
+ generate_mock_once('HTMLPurifier_DefinitionCacheFactory');
+ $factory_mock = new HTMLPurifier_DefinitionCacheFactoryMock();
+ $old = HTMLPurifier_DefinitionCacheFactory::instance();
+ HTMLPurifier_DefinitionCacheFactory::instance($factory_mock);
+ $factory_mock->setReturnValue('create', $cache_mock);
+
+ $this->assertDef('http://example.com');
+
+ HTMLPurifier_DefinitionCacheFactory::instance($old);
+ }
+
+ function test_make() {
+ $factory = new HTMLPurifier_AttrDef_URI();
+ $def = $factory->make('');
+ $def2 = new HTMLPurifier_AttrDef_URI();
+ $this->assertIdentical($def, $def2);
+
+ $def = $factory->make('embedded');
+ $def2 = new HTMLPurifier_AttrDef_URI(true);
+ $this->assertIdentical($def, $def2);
+ }
+
+ /*
+ function test_validate_configWhitelist() {
+
+ $this->config->set('URI.HostPolicy', 'DenyAll');
+ $this->config->set('URI.HostWhitelist', array(null, 'google.com'));
+
+ $this->assertDef('http://example.com/fo/google.com', false);
+ $this->assertDef('server.txt');
+ $this->assertDef('ftp://www.google.com/?t=a');
+ $this->assertDef('http://google.com.tricky.spamsite.net', false);
+
+ }
+ */
+
+}
+
+// vim: et sw=4 sts=4
diff --git a/lib/htmlpurifier/tests/HTMLPurifier/AttrDefHarness.php b/lib/htmlpurifier/tests/HTMLPurifier/AttrDefHarness.php
new file mode 100644
index 000000000..b45b0ca53
--- /dev/null
+++ b/lib/htmlpurifier/tests/HTMLPurifier/AttrDefHarness.php
@@ -0,0 +1,27 @@
+config = HTMLPurifier_Config::createDefault();
+ $this->context = new HTMLPurifier_Context();
+ }
+
+ // cannot be used for accumulator
+ function assertDef($string, $expect = true) {
+ // $expect can be a string or bool
+ $result = $this->def->validate($string, $this->config, $this->context);
+ if ($expect === true) {
+ $this->assertIdentical($string, $result);
+ } else {
+ $this->assertIdentical($expect, $result);
+ }
+ }
+
+}
+
+// vim: et sw=4 sts=4
diff --git a/lib/htmlpurifier/tests/HTMLPurifier/AttrDefTest.php b/lib/htmlpurifier/tests/HTMLPurifier/AttrDefTest.php
new file mode 100644
index 000000000..d7466e37d
--- /dev/null
+++ b/lib/htmlpurifier/tests/HTMLPurifier/AttrDefTest.php
@@ -0,0 +1,32 @@
+assertIdentical('', $def->parseCDATA(''));
+ $this->assertIdentical('', $def->parseCDATA("\t\n\r \t\t"));
+ $this->assertIdentical('foo', $def->parseCDATA("\t\n\r foo\t\t"));
+ $this->assertIdentical('translate to space', $def->parseCDATA("translate\nto\tspace"));
+
+ }
+
+ function test_make() {
+
+ $def = new HTMLPurifier_AttrDefTestable();
+ $def2 = $def->make('');
+ $this->assertIdentical($def, $def2);
+
+ }
+
+}
+
+// vim: et sw=4 sts=4
diff --git a/lib/htmlpurifier/tests/HTMLPurifier/AttrTransform/BackgroundTest.php b/lib/htmlpurifier/tests/HTMLPurifier/AttrTransform/BackgroundTest.php
new file mode 100644
index 000000000..0730ab4bc
--- /dev/null
+++ b/lib/htmlpurifier/tests/HTMLPurifier/AttrTransform/BackgroundTest.php
@@ -0,0 +1,40 @@
+obj = new HTMLPurifier_AttrTransform_Background();
+ }
+
+ function testEmptyInput() {
+ $this->assertResult( array() );
+ }
+
+ function testBasicTransform() {
+ $this->assertResult(
+ array('background' => 'logo.png'),
+ array('style' => 'background-image:url(logo.png);')
+ );
+ }
+
+ function testPrependNewCSS() {
+ $this->assertResult(
+ array('background' => 'logo.png', 'style' => 'font-weight:bold'),
+ array('style' => 'background-image:url(logo.png);font-weight:bold')
+ );
+ }
+
+ function testLenientTreatmentOfInvalidInput() {
+ // notice that we rely on the CSS validator later to fix this invalid
+ // stuff
+ $this->assertResult(
+ array('background' => 'logo.png);foo:('),
+ array('style' => 'background-image:url(logo.png);foo:();')
+ );
+ }
+
+}
+
+// vim: et sw=4 sts=4
diff --git a/lib/htmlpurifier/tests/HTMLPurifier/AttrTransform/BdoDirTest.php b/lib/htmlpurifier/tests/HTMLPurifier/AttrTransform/BdoDirTest.php
new file mode 100644
index 000000000..cdf6f8a9b
--- /dev/null
+++ b/lib/htmlpurifier/tests/HTMLPurifier/AttrTransform/BdoDirTest.php
@@ -0,0 +1,30 @@
+obj = new HTMLPurifier_AttrTransform_BdoDir();
+ }
+
+ function testAddDefaultDir() {
+ $this->assertResult( array(), array('dir' => 'ltr') );
+ }
+
+ function testPreserveExistingDir() {
+ $this->assertResult( array('dir' => 'rtl') );
+ }
+
+ function testAlternateDefault() {
+ $this->config->set('Attr.DefaultTextDir', 'rtl');
+ $this->assertResult(
+ array(),
+ array('dir' => 'rtl')
+ );
+
+ }
+
+}
+
+// vim: et sw=4 sts=4
diff --git a/lib/htmlpurifier/tests/HTMLPurifier/AttrTransform/BgColorTest.php b/lib/htmlpurifier/tests/HTMLPurifier/AttrTransform/BgColorTest.php
new file mode 100644
index 000000000..13567b74e
--- /dev/null
+++ b/lib/htmlpurifier/tests/HTMLPurifier/AttrTransform/BgColorTest.php
@@ -0,0 +1,44 @@
+obj = new HTMLPurifier_AttrTransform_BgColor();
+ }
+
+ function testEmptyInput() {
+ $this->assertResult( array() );
+ }
+
+ function testBasicTransform() {
+ $this->assertResult(
+ array('bgcolor' => '#000000'),
+ array('style' => 'background-color:#000000;')
+ );
+ }
+
+ function testPrependNewCSS() {
+ $this->assertResult(
+ array('bgcolor' => '#000000', 'style' => 'font-weight:bold'),
+ array('style' => 'background-color:#000000;font-weight:bold')
+ );
+ }
+
+ function testLenientTreatmentOfInvalidInput() {
+ // this may change when we natively support the datatype and
+ // validate its contents before forwarding it on
+ $this->assertResult(
+ array('bgcolor' => '#F00'),
+ array('style' => 'background-color:#F00;')
+ );
+ }
+
+}
+
+// vim: et sw=4 sts=4
diff --git a/lib/htmlpurifier/tests/HTMLPurifier/AttrTransform/BoolToCSSTest.php b/lib/htmlpurifier/tests/HTMLPurifier/AttrTransform/BoolToCSSTest.php
new file mode 100644
index 000000000..73f9d6b86
--- /dev/null
+++ b/lib/htmlpurifier/tests/HTMLPurifier/AttrTransform/BoolToCSSTest.php
@@ -0,0 +1,38 @@
+obj = new HTMLPurifier_AttrTransform_BoolToCSS('foo', 'bar:3in;');
+ }
+
+ function testEmptyInput() {
+ $this->assertResult( array() );
+ }
+
+ function testBasicTransform() {
+ $this->assertResult(
+ array('foo' => 'foo'),
+ array('style' => 'bar:3in;')
+ );
+ }
+
+ function testIgnoreValueOfBooleanAttribute() {
+ $this->assertResult(
+ array('foo' => 'no'),
+ array('style' => 'bar:3in;')
+ );
+ }
+
+ function testPrependCSS() {
+ $this->assertResult(
+ array('foo' => 'foo', 'style' => 'background-color:#F00;'),
+ array('style' => 'bar:3in;background-color:#F00;')
+ );
+ }
+
+}
+
+// vim: et sw=4 sts=4
diff --git a/lib/htmlpurifier/tests/HTMLPurifier/AttrTransform/BorderTest.php b/lib/htmlpurifier/tests/HTMLPurifier/AttrTransform/BorderTest.php
new file mode 100644
index 000000000..d10aa28e6
--- /dev/null
+++ b/lib/htmlpurifier/tests/HTMLPurifier/AttrTransform/BorderTest.php
@@ -0,0 +1,38 @@
+obj = new HTMLPurifier_AttrTransform_Border();
+ }
+
+ function testEmptyInput() {
+ $this->assertResult( array() );
+ }
+
+ function testBasicTransform() {
+ $this->assertResult(
+ array('border' => '1'),
+ array('style' => 'border:1px solid;')
+ );
+ }
+
+ function testLenientTreatmentOfInvalidInput() {
+ $this->assertResult(
+ array('border' => '10%'),
+ array('style' => 'border:10%px solid;')
+ );
+ }
+
+ function testPrependNewCSS() {
+ $this->assertResult(
+ array('border' => '23', 'style' => 'font-weight:bold;'),
+ array('style' => 'border:23px solid;font-weight:bold;')
+ );
+ }
+
+}
+
+// vim: et sw=4 sts=4
diff --git a/lib/htmlpurifier/tests/HTMLPurifier/AttrTransform/EnumToCSSTest.php b/lib/htmlpurifier/tests/HTMLPurifier/AttrTransform/EnumToCSSTest.php
new file mode 100644
index 000000000..f0381fe88
--- /dev/null
+++ b/lib/htmlpurifier/tests/HTMLPurifier/AttrTransform/EnumToCSSTest.php
@@ -0,0 +1,73 @@
+obj = new HTMLPurifier_AttrTransform_EnumToCSS('align', array(
+ 'left' => 'text-align:left;',
+ 'right' => 'text-align:right;'
+ ));
+ }
+
+ function testEmptyInput() {
+ $this->assertResult( array() );
+ }
+
+ function testPreserveArraysWithoutInterestingAttributes() {
+ $this->assertResult( array('style' => 'font-weight:bold;') );
+ }
+
+ function testConvertAlignLeft() {
+ $this->assertResult(
+ array('align' => 'left'),
+ array('style' => 'text-align:left;')
+ );
+ }
+
+ function testConvertAlignRight() {
+ $this->assertResult(
+ array('align' => 'right'),
+ array('style' => 'text-align:right;')
+ );
+ }
+
+ function testRemoveInvalidAlign() {
+ $this->assertResult(
+ array('align' => 'invalid'),
+ array()
+ );
+ }
+
+ function testPrependNewCSS() {
+ $this->assertResult(
+ array('align' => 'left', 'style' => 'font-weight:bold;'),
+ array('style' => 'text-align:left;font-weight:bold;')
+ );
+
+ }
+
+ function testCaseInsensitive() {
+ $this->obj = new HTMLPurifier_AttrTransform_EnumToCSS('align', array(
+ 'right' => 'text-align:right;'
+ ));
+ $this->assertResult(
+ array('align' => 'RIGHT'),
+ array('style' => 'text-align:right;')
+ );
+ }
+
+ function testCaseSensitive() {
+ $this->obj = new HTMLPurifier_AttrTransform_EnumToCSS('align', array(
+ 'right' => 'text-align:right;'
+ ), true);
+ $this->assertResult(
+ array('align' => 'RIGHT'),
+ array()
+ );
+ }
+
+}
+
+// vim: et sw=4 sts=4
diff --git a/lib/htmlpurifier/tests/HTMLPurifier/AttrTransform/ImgRequiredTest.php b/lib/htmlpurifier/tests/HTMLPurifier/AttrTransform/ImgRequiredTest.php
new file mode 100644
index 000000000..99f0a03e9
--- /dev/null
+++ b/lib/htmlpurifier/tests/HTMLPurifier/AttrTransform/ImgRequiredTest.php
@@ -0,0 +1,55 @@
+obj = new HTMLPurifier_AttrTransform_ImgRequired();
+ }
+
+ function testAddMissingAttr() {
+ $this->config->set('Core.RemoveInvalidImg', false);
+ $this->assertResult(
+ array(),
+ array('src' => '', 'alt' => 'Invalid image')
+ );
+ }
+
+ function testAlternateDefaults() {
+ $this->config->set('Attr.DefaultInvalidImage', 'blank.png');
+ $this->config->set('Attr.DefaultInvalidImageAlt', 'Pawned!');
+ $this->config->set('Attr.DefaultImageAlt', 'not pawned');
+ $this->config->set('Core.RemoveInvalidImg', false);
+ $this->assertResult(
+ array(),
+ array('src' => 'blank.png', 'alt' => 'Pawned!')
+ );
+ }
+
+ function testGenerateAlt() {
+ $this->assertResult(
+ array('src' => '/path/to/foobar.png'),
+ array('src' => '/path/to/foobar.png', 'alt' => 'foobar.png')
+ );
+ }
+
+ function testAddDefaultSrc() {
+ $this->config->set('Core.RemoveInvalidImg', false);
+ $this->assertResult(
+ array('alt' => 'intrigue'),
+ array('alt' => 'intrigue', 'src' => '')
+ );
+ }
+
+ function testAddDefaultAlt() {
+ $this->config->set('Attr.DefaultImageAlt', 'default');
+ $this->assertResult(
+ array('src' => ''),
+ array('src' => '', 'alt' => 'default')
+ );
+ }
+
+}
+
+// vim: et sw=4 sts=4
diff --git a/lib/htmlpurifier/tests/HTMLPurifier/AttrTransform/ImgSpaceTest.php b/lib/htmlpurifier/tests/HTMLPurifier/AttrTransform/ImgSpaceTest.php
new file mode 100644
index 000000000..42e8738e4
--- /dev/null
+++ b/lib/htmlpurifier/tests/HTMLPurifier/AttrTransform/ImgSpaceTest.php
@@ -0,0 +1,55 @@
+obj = new HTMLPurifier_AttrTransform_ImgSpace('vspace');
+ }
+
+ function testEmptyInput() {
+ $this->assertResult( array() );
+ }
+
+ function testVerticalBasicUsage() {
+ $this->assertResult(
+ array('vspace' => '1'),
+ array('style' => 'margin-top:1px;margin-bottom:1px;')
+ );
+ }
+
+ function testLenientHandlingOfInvalidInput() {
+ $this->assertResult(
+ array('vspace' => '10%'),
+ array('style' => 'margin-top:10%px;margin-bottom:10%px;')
+ );
+ }
+
+ function testPrependNewCSS() {
+ $this->assertResult(
+ array('vspace' => '23', 'style' => 'font-weight:bold;'),
+ array('style' => 'margin-top:23px;margin-bottom:23px;font-weight:bold;')
+ );
+ }
+
+ function testHorizontalBasicUsage() {
+ $this->obj = new HTMLPurifier_AttrTransform_ImgSpace('hspace');
+ $this->assertResult(
+ array('hspace' => '1'),
+ array('style' => 'margin-left:1px;margin-right:1px;')
+ );
+ }
+
+ function testInvalidConstructionParameter() {
+ $this->expectError('ispace is not valid space attribute');
+ $this->obj = new HTMLPurifier_AttrTransform_ImgSpace('ispace');
+ $this->assertResult(
+ array('ispace' => '1'),
+ array()
+ );
+ }
+
+}
+
+// vim: et sw=4 sts=4
diff --git a/lib/htmlpurifier/tests/HTMLPurifier/AttrTransform/InputTest.php b/lib/htmlpurifier/tests/HTMLPurifier/AttrTransform/InputTest.php
new file mode 100644
index 000000000..2603ff1be
--- /dev/null
+++ b/lib/htmlpurifier/tests/HTMLPurifier/AttrTransform/InputTest.php
@@ -0,0 +1,94 @@
+obj = new HTMLPurifier_AttrTransform_Input();
+ }
+
+ function testEmptyInput() {
+ $this->assertResult(array());
+ }
+
+ function testInvalidCheckedWithEmpty() {
+ $this->assertResult(array('checked' => 'checked'), array());
+ }
+
+ function testInvalidCheckedWithPassword() {
+ $this->assertResult(array(
+ 'checked' => 'checked',
+ 'type' => 'password'
+ ), array(
+ 'type' => 'password'
+ ));
+ }
+
+ function testValidCheckedWithUcCheckbox() {
+ $this->assertResult(array(
+ 'checked' => 'checked',
+ 'type' => 'CHECKBOX',
+ 'value' => 'bar',
+ ));
+ }
+
+ function testInvalidMaxlength() {
+ $this->assertResult(array(
+ 'maxlength' => '10',
+ 'type' => 'checkbox',
+ 'value' => 'foo',
+ ), array(
+ 'type' => 'checkbox',
+ 'value' => 'foo',
+ ));
+ }
+
+ function testValidMaxLength() {
+ $this->assertResult(array(
+ 'maxlength' => '10',
+ ));
+ }
+
+ // these two are really bad test-cases
+
+ function testSizeWithCheckbox() {
+ $this->assertResult(array(
+ 'type' => 'checkbox',
+ 'value' => 'foo',
+ 'size' => '100px',
+ ), array(
+ 'type' => 'checkbox',
+ 'value' => 'foo',
+ 'size' => '100',
+ ));
+ }
+
+ function testSizeWithText() {
+ $this->assertResult(array(
+ 'type' => 'password',
+ 'size' => '100px', // spurious value, to indicate no validation takes place
+ ), array(
+ 'type' => 'password',
+ 'size' => '100px',
+ ));
+ }
+
+ function testInvalidSrc() {
+ $this->assertResult(array(
+ 'src' => 'img.png',
+ ), array());
+ }
+
+ function testMissingValue() {
+ $this->assertResult(array(
+ 'type' => 'checkbox',
+ ), array(
+ 'type' => 'checkbox',
+ 'value' => '',
+ ));
+ }
+
+}
+
+// vim: et sw=4 sts=4
diff --git a/lib/htmlpurifier/tests/HTMLPurifier/AttrTransform/LangTest.php b/lib/htmlpurifier/tests/HTMLPurifier/AttrTransform/LangTest.php
new file mode 100644
index 000000000..960ad20a0
--- /dev/null
+++ b/lib/htmlpurifier/tests/HTMLPurifier/AttrTransform/LangTest.php
@@ -0,0 +1,46 @@
+obj = new HTMLPurifier_AttrTransform_Lang();
+ }
+
+ function testEmptyInput() {
+ $this->assertResult(array());
+ }
+
+ function testCopyLangToXMLLang() {
+ $this->assertResult(
+ array('lang' => 'en'),
+ array('lang' => 'en', 'xml:lang' => 'en')
+ );
+ }
+
+ function testPreserveAttributes() {
+ $this->assertResult(
+ array('src' => 'vert.png', 'lang' => 'fr'),
+ array('src' => 'vert.png', 'lang' => 'fr', 'xml:lang' => 'fr')
+ );
+ }
+
+ function testCopyXMLLangToLang() {
+ $this->assertResult(
+ array('xml:lang' => 'en'),
+ array('xml:lang' => 'en', 'lang' => 'en')
+ );
+ }
+
+ function testXMLLangOverridesLang() {
+ $this->assertResult(
+ array('lang' => 'fr', 'xml:lang' => 'de'),
+ array('lang' => 'de', 'xml:lang' => 'de')
+ );
+ }
+
+}
+
+// vim: et sw=4 sts=4
diff --git a/lib/htmlpurifier/tests/HTMLPurifier/AttrTransform/LengthTest.php b/lib/htmlpurifier/tests/HTMLPurifier/AttrTransform/LengthTest.php
new file mode 100644
index 000000000..3fbaa0ccf
--- /dev/null
+++ b/lib/htmlpurifier/tests/HTMLPurifier/AttrTransform/LengthTest.php
@@ -0,0 +1,45 @@
+obj = new HTMLPurifier_AttrTransform_Length('width');
+ }
+
+ function testEmptyInput() {
+ $this->assertResult( array() );
+ }
+
+ function testTransformPixel() {
+ $this->assertResult(
+ array('width' => '10'),
+ array('style' => 'width:10px;')
+ );
+ }
+
+ function testTransformPercentage() {
+ $this->assertResult(
+ array('width' => '10%'),
+ array('style' => 'width:10%;')
+ );
+ }
+
+ function testPrependNewCSS() {
+ $this->assertResult(
+ array('width' => '10%', 'style' => 'font-weight:bold'),
+ array('style' => 'width:10%;font-weight:bold')
+ );
+ }
+
+ function testLenientTreatmentOfInvalidInput() {
+ $this->assertResult(
+ array('width' => 'asdf'),
+ array('style' => 'width:asdf;')
+ );
+ }
+
+}
+
+// vim: et sw=4 sts=4
diff --git a/lib/htmlpurifier/tests/HTMLPurifier/AttrTransform/NameSyncTest.php b/lib/htmlpurifier/tests/HTMLPurifier/AttrTransform/NameSyncTest.php
new file mode 100644
index 000000000..bae4a8d03
--- /dev/null
+++ b/lib/htmlpurifier/tests/HTMLPurifier/AttrTransform/NameSyncTest.php
@@ -0,0 +1,40 @@
+obj = new HTMLPurifier_AttrTransform_NameSync();
+ $this->accumulator = new HTMLPurifier_IDAccumulator();
+ $this->context->register('IDAccumulator', $this->accumulator);
+ $this->config->set('Attr.EnableID', true);
+ }
+
+ function testEmpty() {
+ $this->assertResult( array() );
+ }
+
+ function testAllowSame() {
+ $this->assertResult(
+ array('name' => 'free', 'id' => 'free')
+ );
+ }
+
+ function testAllowDifferent() {
+ $this->assertResult(
+ array('name' => 'tryit', 'id' => 'thisgood')
+ );
+ }
+
+ function testCheckName() {
+ $this->accumulator->add('notok');
+ $this->assertResult(
+ array('name' => 'notok', 'id' => 'ok'),
+ array('id' => 'ok')
+ );
+ }
+
+}
+
+// vim: et sw=4 sts=4
diff --git a/lib/htmlpurifier/tests/HTMLPurifier/AttrTransform/NameTest.php b/lib/htmlpurifier/tests/HTMLPurifier/AttrTransform/NameTest.php
new file mode 100644
index 000000000..10e121238
--- /dev/null
+++ b/lib/htmlpurifier/tests/HTMLPurifier/AttrTransform/NameTest.php
@@ -0,0 +1,31 @@
+obj = new HTMLPurifier_AttrTransform_Name();
+ }
+
+ function testEmpty() {
+ $this->assertResult( array() );
+ }
+
+ function testTransformNameToID() {
+ $this->assertResult(
+ array('name' => 'free'),
+ array('id' => 'free')
+ );
+ }
+
+ function testExistingIDOverridesName() {
+ $this->assertResult(
+ array('name' => 'tryit', 'id' => 'tobad'),
+ array('id' => 'tobad')
+ );
+ }
+
+}
+
+// vim: et sw=4 sts=4
diff --git a/lib/htmlpurifier/tests/HTMLPurifier/AttrTransformHarness.php b/lib/htmlpurifier/tests/HTMLPurifier/AttrTransformHarness.php
new file mode 100644
index 000000000..d43c0108f
--- /dev/null
+++ b/lib/htmlpurifier/tests/HTMLPurifier/AttrTransformHarness.php
@@ -0,0 +1,13 @@
+func = 'transform';
+ }
+
+}
+
+// vim: et sw=4 sts=4
diff --git a/lib/htmlpurifier/tests/HTMLPurifier/AttrTransformTest.php b/lib/htmlpurifier/tests/HTMLPurifier/AttrTransformTest.php
new file mode 100644
index 000000000..71a788580
--- /dev/null
+++ b/lib/htmlpurifier/tests/HTMLPurifier/AttrTransformTest.php
@@ -0,0 +1,45 @@
+prependCSS($attr, 'style:new;');
+ $this->assertIdentical(array('style' => 'style:new;'), $attr);
+
+ $attr = array('style' => 'style:original;');
+ $t->prependCSS($attr, 'style:new;');
+ $this->assertIdentical(array('style' => 'style:new;style:original;'), $attr);
+
+ $attr = array('style' => 'style:original;', 'misc' => 'un-related');
+ $t->prependCSS($attr, 'style:new;');
+ $this->assertIdentical(array('style' => 'style:new;style:original;', 'misc' => 'un-related'), $attr);
+
+ }
+
+ function test_confiscateAttr() {
+
+ $t = new HTMLPurifier_AttrTransformTestable();
+
+ $attr = array('flavor' => 'sweet');
+ $this->assertIdentical('sweet', $t->confiscateAttr($attr, 'flavor'));
+ $this->assertIdentical(array(), $attr);
+
+ $attr = array('flavor' => 'sweet');
+ $this->assertIdentical(null, $t->confiscateAttr($attr, 'color'));
+ $this->assertIdentical(array('flavor' => 'sweet'), $attr);
+
+ }
+
+}
+
+// vim: et sw=4 sts=4
diff --git a/lib/htmlpurifier/tests/HTMLPurifier/AttrTypesTest.php b/lib/htmlpurifier/tests/HTMLPurifier/AttrTypesTest.php
new file mode 100644
index 000000000..d1ae43709
--- /dev/null
+++ b/lib/htmlpurifier/tests/HTMLPurifier/AttrTypesTest.php
@@ -0,0 +1,26 @@
+assertIdentical(
+ $types->get('CDATA'),
+ new HTMLPurifier_AttrDef_Text()
+ );
+
+ $this->expectError('Cannot retrieve undefined attribute type foobar');
+ $types->get('foobar');
+
+ $this->assertIdentical(
+ $types->get('Enum#foo,bar'),
+ new HTMLPurifier_AttrDef_Enum(array('foo', 'bar'))
+ );
+
+ }
+
+}
+
+// vim: et sw=4 sts=4
diff --git a/lib/htmlpurifier/tests/HTMLPurifier/AttrValidator_ErrorsTest.php b/lib/htmlpurifier/tests/HTMLPurifier/AttrValidator_ErrorsTest.php
new file mode 100644
index 000000000..307d3292b
--- /dev/null
+++ b/lib/htmlpurifier/tests/HTMLPurifier/AttrValidator_ErrorsTest.php
@@ -0,0 +1,66 @@
+language = HTMLPurifier_LanguageFactory::instance()->create($config, $this->context);
+ $this->context->register('Locale', $this->language);
+ $this->collector = new HTMLPurifier_ErrorCollector($this->context);
+ $this->context->register('Generator', new HTMLPurifier_Generator($config, $this->context));
+ }
+
+ protected function invoke($input) {
+ $validator = new HTMLPurifier_AttrValidator();
+ $validator->validateToken($input, $this->config, $this->context);
+ }
+
+ function testAttributesTransformedGlobalPre() {
+ $def = $this->config->getHTMLDefinition(true);
+ generate_mock_once('HTMLPurifier_AttrTransform');
+ $transform = new HTMLPurifier_AttrTransformMock();
+ $input = array('original' => 'value');
+ $output = array('class' => 'value'); // must be valid
+ $transform->setReturnValue('transform', $output, array($input, new AnythingExpectation(), new AnythingExpectation()));
+ $def->info_attr_transform_pre[] = $transform;
+
+ $token = new HTMLPurifier_Token_Start('span', $input, 1);
+ $this->invoke($token);
+
+ $result = $this->collector->getRaw();
+ $expect = array(
+ array(1, E_NOTICE, 'Attributes on transformed from original to class', array()),
+ );
+ $this->assertIdentical($result, $expect);
+ }
+
+ function testAttributesTransformedLocalPre() {
+ $this->config->set('HTML.TidyLevel', 'heavy');
+ $input = array('align' => 'right');
+ $output = array('style' => 'text-align:right;');
+ $token = new HTMLPurifier_Token_Start('p', $input, 1);
+ $this->invoke($token);
+ $result = $this->collector->getRaw();
+ $expect = array(
+ array(1, E_NOTICE, 'Attributes on
transformed from align to style', array()),
+ );
+ $this->assertIdentical($result, $expect);
+ }
+
+ // too lazy to check for global post and global pre
+
+ function testAttributeRemoved() {
+ $token = new HTMLPurifier_Token_Start('p', array('foobar' => 'right'), 1);
+ $this->invoke($token);
+ $result = $this->collector->getRaw();
+ $expect = array(
+ array(1, E_ERROR, 'foobar attribute on
removed', array()),
+ );
+ $this->assertIdentical($result, $expect);
+ }
+
+}
+
+// vim: et sw=4 sts=4
diff --git a/lib/htmlpurifier/tests/HTMLPurifier/ChildDef/ChameleonTest.php b/lib/htmlpurifier/tests/HTMLPurifier/ChildDef/ChameleonTest.php
new file mode 100644
index 000000000..82493f40e
--- /dev/null
+++ b/lib/htmlpurifier/tests/HTMLPurifier/ChildDef/ChameleonTest.php
@@ -0,0 +1,40 @@
+obj = new HTMLPurifier_ChildDef_Chameleon(
+ 'b | i', // allowed only when in inline context
+ 'b | i | div' // allowed only when in block context
+ );
+ $this->context->register('IsInline', $this->isInline);
+ }
+
+ function testInlineAlwaysAllowed() {
+ $this->isInline = true;
+ $this->assertResult(
+ 'Allowed.'
+ );
+ }
+
+ function testBlockNotAllowedInInline() {
+ $this->isInline = true;
+ $this->assertResult(
+ '
'
+ );
+ }
+
+ function testNestedAlternateSpans() {
+ $this->assertResult(
+'a b c d e f
+',
+'a b c d e f
+'
+ );
+ }
+
+ function testSpanWithSomeInvalidAttributes() {
+ $this->assertResult(
+ '