schema = new HTMLPurifier_ConfigSchema (); } // test functionality based on ConfigSchema public function testNormal() { $this->schema->add ( 'Element.Abbr', 'H', 'string', false ); $this->schema->add ( 'Element.Name', 'hydrogen', 'istring', false ); $this->schema->add ( 'Element.Number', 1, 'int', false ); $this->schema->add ( 'Element.Mass', 1.00794, 'float', false ); $this->schema->add ( 'Element.Radioactive', false, 'bool', false ); $this->schema->add ( 'Element.Isotopes', array ( 1 => true, 2 => true, 3 => true ), 'lookup', false ); $this->schema->add ( 'Element.Traits', array ( 'nonmetallic', 'odorless', 'flammable' ), 'list', false ); $this->schema->add ( 'Element.IsotopeNames', array ( 1 => 'protium', 2 => 'deuterium', 3 => 'tritium' ), 'hash', false ); $this->schema->add ( 'Element.Object', new stdClass (), 'mixed', false ); $config = new HTMLPurifier_Config ( $this->schema ); $config->autoFinalize = false; $config->chatty = false; // test default value retrieval $this->assertIdentical ( $config->get ( 'Element.Abbr' ), 'H' ); $this->assertIdentical ( $config->get ( 'Element.Name' ), 'hydrogen' ); $this->assertIdentical ( $config->get ( 'Element.Number' ), 1 ); $this->assertIdentical ( $config->get ( 'Element.Mass' ), 1.00794 ); $this->assertIdentical ( $config->get ( 'Element.Radioactive' ), false ); $this->assertIdentical ( $config->get ( 'Element.Isotopes' ), array ( 1 => true, 2 => true, 3 => true ) ); $this->assertIdentical ( $config->get ( 'Element.Traits' ), array ( 'nonmetallic', 'odorless', 'flammable' ) ); $this->assertIdentical ( $config->get ( 'Element.IsotopeNames' ), array ( 1 => 'protium', 2 => 'deuterium', 3 => 'tritium' ) ); $this->assertIdentical ( $config->get ( 'Element.Object' ), new stdClass () ); // test setting values $config->set ( 'Element.Abbr', 'Pu' ); $config->set ( 'Element.Name', 'PLUTONIUM' ); // test decaps $config->set ( 'Element.Number', '94' ); // test parsing $config->set ( 'Element.Mass', '244.' ); // test parsing $config->set ( 'Element.Radioactive', true ); $config->set ( 'Element.Isotopes', array ( 238, 239 ) ); // test inversion $config->set ( 'Element.Traits', 'nuclear, heavy, actinide' ); // test parsing $config->set ( 'Element.IsotopeNames', array ( 238 => 'Plutonium-238', 239 => 'Plutonium-239' ) ); $config->set ( 'Element.Object', false ); // unmodeled $this->expectError ( 'Cannot set undefined directive Element.Metal to value' ); $config->set ( 'Element.Metal', true ); $this->expectError ( 'Value for Element.Radioactive is of invalid type, should be bool' ); $config->set ( 'Element.Radioactive', 'very' ); // test value retrieval $this->assertIdentical ( $config->get ( 'Element.Abbr' ), 'Pu' ); $this->assertIdentical ( $config->get ( 'Element.Name' ), 'plutonium' ); $this->assertIdentical ( $config->get ( 'Element.Number' ), 94 ); $this->assertIdentical ( $config->get ( 'Element.Mass' ), 244. ); $this->assertIdentical ( $config->get ( 'Element.Radioactive' ), true ); $this->assertIdentical ( $config->get ( 'Element.Isotopes' ), array ( 238 => true, 239 => true ) ); $this->assertIdentical ( $config->get ( 'Element.Traits' ), array ( 'nuclear', 'heavy', 'actinide' ) ); $this->assertIdentical ( $config->get ( 'Element.IsotopeNames' ), array ( 238 => 'Plutonium-238', 239 => 'Plutonium-239' ) ); $this->assertIdentical ( $config->get ( 'Element.Object' ), false ); $this->expectError ( 'Cannot retrieve value of undefined directive Element.Metal' ); $config->get ( 'Element.Metal' ); } public function testEnumerated() { // case sensitive $this->schema->add ( 'Instrument.Manufacturer', 'Yamaha', 'string', false ); $this->schema->addAllowedValues ( 'Instrument.Manufacturer', array ( 'Yamaha' => true, 'Conn-Selmer' => true, 'Vandoren' => true, 'Laubin' => true, 'Buffet' => true, 'other' => true ) ); $this->schema->addValueAliases ( 'Instrument.Manufacturer', array ( 'Selmer' => 'Conn-Selmer' ) ); // case insensitive $this->schema->add ( 'Instrument.Family', 'woodwind', 'istring', false ); $this->schema->addAllowedValues ( 'Instrument.Family', array ( 'brass' => true, 'woodwind' => true, 'percussion' => true, 'string' => true, 'keyboard' => true, 'electronic' => true ) ); $this->schema->addValueAliases ( 'Instrument.Family', array ( 'synth' => 'electronic' ) ); $config = new HTMLPurifier_Config ( $this->schema ); $config->autoFinalize = false; $config->chatty = false; // case sensitive $config->set ( 'Instrument.Manufacturer', 'Vandoren' ); $this->assertIdentical ( $config->get ( 'Instrument.Manufacturer' ), 'Vandoren' ); $config->set ( 'Instrument.Manufacturer', 'Selmer' ); $this->assertIdentical ( $config->get ( 'Instrument.Manufacturer' ), 'Conn-Selmer' ); $this->expectError ( 'Value not supported, valid values are: Yamaha, Conn-Selmer, Vandoren, Laubin, Buffet, other' ); $config->set ( 'Instrument.Manufacturer', 'buffet' ); // case insensitive $config->set ( 'Instrument.Family', 'brass' ); $this->assertIdentical ( $config->get ( 'Instrument.Family' ), 'brass' ); $config->set ( 'Instrument.Family', 'PERCUSSION' ); $this->assertIdentical ( $config->get ( 'Instrument.Family' ), 'percussion' ); $config->set ( 'Instrument.Family', 'synth' ); $this->assertIdentical ( $config->get ( 'Instrument.Family' ), 'electronic' ); $config->set ( 'Instrument.Family', 'Synth' ); $this->assertIdentical ( $config->get ( 'Instrument.Family' ), 'electronic' ); } public function testNull() { $this->schema->add ( 'ReportCard.English', null, 'string', true ); $this->schema->add ( 'ReportCard.Absences', 0, 'int', false ); $config = new HTMLPurifier_Config ( $this->schema ); $config->autoFinalize = false; $config->chatty = false; $config->set ( 'ReportCard.English', 'B-' ); $this->assertIdentical ( $config->get ( 'ReportCard.English' ), 'B-' ); $config->set ( 'ReportCard.English', null ); // not yet graded $this->assertIdentical ( $config->get ( 'ReportCard.English' ), null ); // error $this->expectError ( 'Value for ReportCard.Absences is of invalid type, should be int' ); $config->set ( 'ReportCard.Absences', null ); } public function testAliases() { $this->schema->add ( 'Home.Rug', 3, 'int', false ); $this->schema->addAlias ( 'Home.Carpet', 'Home.Rug' ); $config = new HTMLPurifier_Config ( $this->schema ); $config->autoFinalize = false; $config->chatty = false; $this->assertIdentical ( $config->get ( 'Home.Rug' ), 3 ); $this->expectError ( 'Cannot get value from aliased directive, use real name Home.Rug' ); $config->get ( 'Home.Carpet' ); $this->expectError ( 'Home.Carpet is an alias, preferred directive name is Home.Rug' ); $config->set ( 'Home.Carpet', 999 ); $this->assertIdentical ( $config->get ( 'Home.Rug' ), 999 ); } // test functionality based on method public function test_getBatch() { $this->schema->add ( 'Variables.TangentialAcceleration', 'a_tan', 'string', false ); $this->schema->add ( 'Variables.AngularAcceleration', 'alpha', 'string', false ); $config = new HTMLPurifier_Config ( $this->schema ); $config->autoFinalize = false; $config->chatty = false; // grab a namespace $this->assertIdentical ( $config->getBatch ( 'Variables' ), array ( 'TangentialAcceleration' => 'a_tan', 'AngularAcceleration' => 'alpha' ) ); // grab a non-existant namespace $this->expectError ( 'Cannot retrieve undefined namespace Constants' ); $config->getBatch ( 'Constants' ); } public function test_loadIni() { $this->schema->add ( 'Shortcut.Copy', 'c', 'istring', false ); $this->schema->add ( 'Shortcut.Paste', 'v', 'istring', false ); $this->schema->add ( 'Shortcut.Cut', 'x', 'istring', false ); $config = new HTMLPurifier_Config ( $this->schema ); $config->autoFinalize = false; $config->loadIni ( dirname ( __FILE__ ) . '/ConfigTest-loadIni.ini' ); $this->assertIdentical ( $config->get ( 'Shortcut.Copy' ), 'q' ); $this->assertIdentical ( $config->get ( 'Shortcut.Paste' ), 'p' ); $this->assertIdentical ( $config->get ( 'Shortcut.Cut' ), 't' ); } public function test_getHTMLDefinition() { // we actually want to use the old copy, because the definition // generation routines have dependencies on configuration values $config = HTMLPurifier_Config::createDefault (); $config->set ( 'HTML.Doctype', 'XHTML 1.0 Strict' ); $config->autoFinalize = false; $def = $config->getCSSDefinition (); $this->assertIsA ( $def, 'HTMLPurifier_CSSDefinition' ); $def = $config->getHTMLDefinition (); $def2 = $config->getHTMLDefinition (); $this->assertIsA ( $def, 'HTMLPurifier_HTMLDefinition' ); $this->assertTrue ( $def === $def2 ); $this->assertTrue ( $def->setup ); $old_def = clone $def2; $config->set ( 'HTML.Doctype', 'HTML 4.01 Transitional' ); $def = $config->getHTMLDefinition (); $this->assertIsA ( $def, 'HTMLPurifier_HTMLDefinition' ); $this->assertTrue ( $def !== $old_def ); $this->assertTrue ( $def->setup ); } public function test_getHTMLDefinition_deprecatedRawError() { $config = HTMLPurifier_Config::createDefault (); $config->chatty = false; // test deprecated retrieval of raw definition $config->set ( 'HTML.DefinitionID', 'HTMLPurifier_ConfigTest->test_getHTMLDefinition()' ); $config->set ( 'HTML.DefinitionRev', 3 ); $this->expectError ( "Useless DefinitionID declaration" ); $def = $config->getHTMLDefinition ( true ); $this->assertEqual ( false, $def->setup ); // auto initialization $config->getHTMLDefinition (); $this->assertTrue ( $def->setup ); } public function test_getHTMLDefinition_optimizedRawError() { $this->expectException ( new HTMLPurifier_Exception ( "Cannot set optimized = true when raw = false" ) ); $config = HTMLPurifier_Config::createDefault (); $config->getHTMLDefinition ( false, true ); } public function test_getHTMLDefinition_rawAfterSetupError() { $this->expectException ( new HTMLPurifier_Exception ( "Cannot retrieve raw definition after it has already been setup" ) ); $config = HTMLPurifier_Config::createDefault (); $config->chatty = false; $config->getHTMLDefinition (); $config->getHTMLDefinition ( true ); } public function test_getHTMLDefinition_inconsistentOptimizedError() { $this->expectError ( "Useless DefinitionID declaration" ); $this->expectException ( new HTMLPurifier_Exception ( "Inconsistent use of optimized and unoptimized raw definition retrievals" ) ); $config = HTMLPurifier_Config::create ( array ( 'HTML.DefinitionID' => 'HTMLPurifier_ConfigTest->test_getHTMLDefinition_inconsistentOptimizedError' ) ); $config->chatty = false; $config->getHTMLDefinition ( true, false ); $config->getHTMLDefinition ( true, true ); } public function test_getHTMLDefinition_inconsistentOptimizedError2() { $this->expectException ( new HTMLPurifier_Exception ( "Inconsistent use of optimized and unoptimized raw definition retrievals" ) ); $config = HTMLPurifier_Config::create ( array ( 'HTML.DefinitionID' => 'HTMLPurifier_ConfigTest->test_getHTMLDefinition_inconsistentOptimizedError2' ) ); $config->chatty = false; $config->getHTMLDefinition ( true, true ); $config->getHTMLDefinition ( true, false ); } public function test_getHTMLDefinition_rawError() { $config = HTMLPurifier_Config::createDefault (); $this->expectException ( new HTMLPurifier_Exception ( 'Cannot retrieve raw version without specifying %HTML.DefinitionID' ) ); $def = $config->getHTMLDefinition ( true, true ); } public function test_getCSSDefinition() { $config = HTMLPurifier_Config::createDefault (); $def = $config->getCSSDefinition (); $this->assertIsA ( $def, 'HTMLPurifier_CSSDefinition' ); } public function test_getDefinition() { $this->schema->add ( 'Cache.DefinitionImpl', null, 'string', true ); $config = new HTMLPurifier_Config ( $this->schema ); $this->expectException ( new HTMLPurifier_Exception ( "Definition of Crust type not supported" ) ); $config->getDefinition ( 'Crust' ); } public function test_loadArray() { // setup a few dummy namespaces/directives for our testing $this->schema->add ( 'Zoo.Aadvark', 0, 'int', false ); $this->schema->add ( 'Zoo.Boar', 0, 'int', false ); $this->schema->add ( 'Zoo.Camel', 0, 'int', false ); $this->schema->add ( 'Zoo.Others', array (), 'list', false ); $config_manual = new HTMLPurifier_Config ( $this->schema ); $config_loadabbr = new HTMLPurifier_Config ( $this->schema ); $config_loadfull = new HTMLPurifier_Config ( $this->schema ); $config_manual->set ( 'Zoo.Aadvark', 3 ); $config_manual->set ( 'Zoo.Boar', 5 ); $config_manual->set ( 'Zoo.Camel', 2000 ); // that's a lotta camels! $config_manual->set ( 'Zoo.Others', array ( 'Peacock', 'Dodo' ) ); // wtf! // condensed form $config_loadabbr->loadArray ( array ( 'Zoo.Aadvark' => 3, 'Zoo.Boar' => 5, 'Zoo.Camel' => 2000, 'Zoo.Others' => array ( 'Peacock', 'Dodo' ) ) ); // fully expanded form $config_loadfull->loadArray ( array ( 'Zoo' => array ( 'Aadvark' => 3, 'Boar' => 5, 'Camel' => 2000, 'Others' => array ( 'Peacock', 'Dodo' ) ) ) ); $this->assertIdentical ( $config_manual, $config_loadabbr ); $this->assertIdentical ( $config_manual, $config_loadfull ); } public function test_create() { $this->schema->add ( 'Cake.Sprinkles', 666, 'int', false ); $this->schema->add ( 'Cake.Flavor', 'vanilla', 'string', false ); $config = new HTMLPurifier_Config ( $this->schema ); $config->set ( 'Cake.Sprinkles', 42 ); // test flat pass-through $created_config = HTMLPurifier_Config::create ( $config, $this->schema ); $this->assertIdentical ( $config, $created_config ); // test loadArray $created_config = HTMLPurifier_Config::create ( array ( 'Cake.Sprinkles' => 42 ), $this->schema ); $this->assertIdentical ( $config, $created_config ); // test loadIni $created_config = HTMLPurifier_Config::create ( dirname ( __FILE__ ) . '/ConfigTest-create.ini', $this->schema ); $this->assertIdentical ( $config, $created_config ); } public function test_finalize() { // test finalization $this->schema->add ( 'Poem.Meter', 'iambic', 'string', false ); $config = new HTMLPurifier_Config ( $this->schema ); $config->autoFinalize = false; $config->chatty = false; $config->set ( 'Poem.Meter', 'irregular' ); $config->finalize (); $this->expectError ( 'Cannot set directive after finalization' ); $config->set ( 'Poem.Meter', 'vedic' ); $this->expectError ( 'Cannot load directives after finalization' ); $config->loadArray ( array ( 'Poem.Meter' => 'octosyllable' ) ); $this->expectError ( 'Cannot load directives after finalization' ); $config->loadIni ( dirname ( __FILE__ ) . '/ConfigTest-finalize.ini' ); } public function test_loadArrayFromForm() { $this->schema->add ( 'Pancake.Mix', 'buttermilk', 'string', false ); $this->schema->add ( 'Pancake.Served', true, 'bool', false ); $this->schema->add ( 'Toppings.Syrup', true, 'bool', false ); $this->schema->add ( 'Toppings.Flavor', 'maple', 'string', false ); $this->schema->add ( 'Toppings.Strawberries', 3, 'int', false ); $this->schema->add ( 'Toppings.Calories', 2000, 'int', true ); $this->schema->add ( 'Toppings.DefinitionID', null, 'string', true ); $this->schema->add ( 'Toppings.DefinitionRev', 1, 'int', false ); $this->schema->add ( 'Toppings.Protected', 1, 'int', false ); $get = array ( 'breakfast' => array ( 'Pancake.Mix' => 'nasty', 'Pancake.Served' => '0', 'Toppings.Syrup' => '0', 'Toppings.Flavor' => "juice", 'Toppings.Strawberries' => '999', 'Toppings.Calories' => '', 'Null_Toppings.Calories' => '1', 'Toppings.DefinitionID' => '', 'Toppings.DefinitionRev' => '65', 'Toppings.Protected' => '4' ) ); $config_expect = HTMLPurifier_Config::create ( array ( 'Pancake.Served' => false, 'Toppings.Syrup' => false, 'Toppings.Flavor' => "juice", 'Toppings.Strawberries' => 999, 'Toppings.Calories' => null ), $this->schema ); $config_result = HTMLPurifier_Config::loadArrayFromForm ( $get, 'breakfast', array ( 'Pancake.Served', 'Toppings', '-Toppings.Protected' ), false, // mq fix $this->schema ); $this->assertEqual ( $config_expect, $config_result ); /* * MAGIC QUOTES NOT TESTED!!! * * $get = array( * 'breakfast' => array( * 'Pancake.Mix' => 'n\\asty' * ) * ); * $config_expect = HTMLPurifier_Config::create(array( * 'Pancake.Mix' => 'n\\asty' * )); * $config_result = HTMLPurifier_Config::loadArrayFromForm($get, 'breakfast', true, false); * $this->assertEqual($config_expect, $config_result); */ } public function test_getAllowedDirectivesForForm() { $this->schema->add ( 'Unused.Unused', 'Foobar', 'string', false ); $this->schema->add ( 'Partial.Allowed', true, 'bool', false ); $this->schema->add ( 'Partial.Unused', 'Foobar', 'string', false ); $this->schema->add ( 'All.Allowed', true, 'bool', false ); $this->schema->add ( 'All.Blacklisted', 'Foobar', 'string', false ); // explicitly blacklisted $this->schema->add ( 'All.DefinitionID', 'Foobar', 'string', true ); // auto-blacklisted $this->schema->add ( 'All.DefinitionRev', 2, 'int', false ); // auto-blacklisted $input = array ( 'Partial.Allowed', 'All', '-All.Blacklisted' ); $output = HTMLPurifier_Config::getAllowedDirectivesForForm ( $input, $this->schema ); $expect = array ( array ( 'Partial', 'Allowed' ), array ( 'All', 'Allowed' ) ); $this->assertEqual ( $output, $expect ); } public function testDeprecatedAPI() { $this->schema->add ( 'Foo.Bar', 2, 'int', false ); $config = new HTMLPurifier_Config ( $this->schema ); $config->chatty = false; $this->expectError ( 'Using deprecated API: use $config->set(\'Foo.Bar\', ...) instead' ); $config->set ( 'Foo', 'Bar', 4 ); $this->expectError ( 'Using deprecated API: use $config->get(\'Foo.Bar\') instead' ); $this->assertIdentical ( $config->get ( 'Foo', 'Bar' ), 4 ); } public function testInherit() { $this->schema->add ( 'Phantom.Masked', 25, 'int', false ); $this->schema->add ( 'Phantom.Unmasked', 89, 'int', false ); $this->schema->add ( 'Phantom.Latemasked', 11, 'int', false ); $config = new HTMLPurifier_Config ( $this->schema ); $config->set ( 'Phantom.Masked', 800 ); $subconfig = HTMLPurifier_Config::inherit ( $config ); $config->set ( 'Phantom.Latemasked', 100, 'int', false ); $this->assertIdentical ( $subconfig->get ( 'Phantom.Masked' ), 800 ); $this->assertIdentical ( $subconfig->get ( 'Phantom.Unmasked' ), 89 ); $this->assertIdentical ( $subconfig->get ( 'Phantom.Latemasked' ), 100 ); } public function testSerialize() { $config = HTMLPurifier_Config::createDefault (); $config->set ( 'HTML.Allowed', 'a' ); $config2 = unserialize ( $config->serialize () ); $this->assertIdentical ( $config->get ( 'HTML.Allowed' ), $config2->get ( 'HTML.Allowed' ) ); } public function testDefinitionCachingNothing() { list ( $mock, $config ) = $this->setupCacheMock ( 'HTML' ); // should not touch the cache $mock->expectNever ( 'get' ); $mock->expectNever ( 'add' ); $mock->expectNever ( 'set' ); $config->getDefinition ( 'HTML', true ); $config->getDefinition ( 'HTML', true ); $config->getDefinition ( 'HTML' ); $this->teardownCacheMock (); } public function testDefinitionCachingOptimized() { list ( $mock, $config ) = $this->setupCacheMock ( 'HTML' ); $mock->expectNever ( 'set' ); $config->set ( 'HTML.DefinitionID', 'HTMLPurifier_ConfigTest->testDefinitionCachingOptimized' ); $mock->expectOnce ( 'get' ); $mock->setReturnValue ( 'get', null ); $this->assertTrue ( $config->maybeGetRawHTMLDefinition () ); $this->assertTrue ( $config->maybeGetRawHTMLDefinition () ); $mock->expectOnce ( 'add' ); $config->getDefinition ( 'HTML' ); $this->teardownCacheMock (); } public function testDefinitionCachingOptimizedHit() { $fake_config = HTMLPurifier_Config::createDefault (); $fake_def = $fake_config->getHTMLDefinition (); list ( $mock, $config ) = $this->setupCacheMock ( 'HTML' ); // should never frob cache $mock->expectNever ( 'add' ); $mock->expectNever ( 'set' ); $config->set ( 'HTML.DefinitionID', 'HTMLPurifier_ConfigTest->testDefinitionCachingOptimizedHit' ); $mock->expectOnce ( 'get' ); $mock->setReturnValue ( 'get', $fake_def ); $this->assertNull ( $config->maybeGetRawHTMLDefinition () ); $config->getDefinition ( 'HTML' ); $config->getDefinition ( 'HTML' ); $this->teardownCacheMock (); } protected function setupCacheMock($type) { // inject our definition cache mock globally (borrowed from // DefinitionFactoryTest) generate_mock_once ( "HTMLPurifier_DefinitionCacheFactory" ); $factory = new HTMLPurifier_DefinitionCacheFactoryMock (); $this->oldFactory = HTMLPurifier_DefinitionCacheFactory::instance (); HTMLPurifier_DefinitionCacheFactory::instance ( $factory ); generate_mock_once ( "HTMLPurifier_DefinitionCache" ); $mock = new HTMLPurifier_DefinitionCacheMock (); $config = HTMLPurifier_Config::createDefault (); $factory->setReturnValue ( 'create', $mock, array ( $type, $config ) ); return array ( $mock, $config ); } protected function teardownCacheMock() { HTMLPurifier_DefinitionCacheFactory::instance ( $this->oldFactory ); } } // vim: et sw=4 sts=4