Update to work with Git version of SimpleTest.
[htmlpurifier.git] / tests / HTMLPurifier / ConfigTest.php
blobdc80bcdbb29a3cdac670e35b52f4ecdd968cda4e
1 <?php
3 class HTMLPurifier_ConfigTest extends HTMLPurifier_Harness
6 protected $schema;
7 protected $oldFactory;
9 public function setUp()
11 // set up a dummy schema object for testing
12 $this->schema = new HTMLPurifier_ConfigSchema();
15 // test functionality based on ConfigSchema
17 public function testNormal()
19 $this->schema->add('Element.Abbr', 'H', 'string', false);
20 $this->schema->add('Element.Name', 'hydrogen', 'istring', false);
21 $this->schema->add('Element.Number', 1, 'int', false);
22 $this->schema->add('Element.Mass', 1.00794, 'float', false);
23 $this->schema->add('Element.Radioactive', false, 'bool', false);
24 $this->schema->add('Element.Isotopes', array(1 => true, 2 => true, 3 => true), 'lookup', false);
25 $this->schema->add('Element.Traits', array('nonmetallic', 'odorless', 'flammable'), 'list', false);
26 $this->schema->add('Element.IsotopeNames', array(1 => 'protium', 2 => 'deuterium', 3 => 'tritium'), 'hash', false);
27 $this->schema->add('Element.Object', new stdClass(), 'mixed', false);
29 $config = new HTMLPurifier_Config($this->schema);
30 $config->autoFinalize = false;
31 $config->chatty = false;
33 // test default value retrieval
34 $this->assertIdentical($config->get('Element.Abbr'), 'H');
35 $this->assertIdentical($config->get('Element.Name'), 'hydrogen');
36 $this->assertIdentical($config->get('Element.Number'), 1);
37 $this->assertIdentical($config->get('Element.Mass'), 1.00794);
38 $this->assertIdentical($config->get('Element.Radioactive'), false);
39 $this->assertIdentical($config->get('Element.Isotopes'), array(1 => true, 2 => true, 3 => true));
40 $this->assertIdentical($config->get('Element.Traits'), array('nonmetallic', 'odorless', 'flammable'));
41 $this->assertIdentical($config->get('Element.IsotopeNames'), array(1 => 'protium', 2 => 'deuterium', 3 => 'tritium'));
42 $this->assertIdentical($config->get('Element.Object'), new stdClass());
44 // test setting values
45 $config->set('Element.Abbr', 'Pu');
46 $config->set('Element.Name', 'PLUTONIUM'); // test decaps
47 $config->set('Element.Number', '94'); // test parsing
48 $config->set('Element.Mass', '244.'); // test parsing
49 $config->set('Element.Radioactive', true);
50 $config->set('Element.Isotopes', array(238, 239)); // test inversion
51 $config->set('Element.Traits', 'nuclear, heavy, actinide'); // test parsing
52 $config->set('Element.IsotopeNames', array(238 => 'Plutonium-238', 239 => 'Plutonium-239'));
53 $config->set('Element.Object', false); // unmodeled
55 $this->expectError('Cannot set undefined directive Element.Metal to value');
56 $config->set('Element.Metal', true);
58 $this->expectError('Value for Element.Radioactive is of invalid type, should be bool');
59 $config->set('Element.Radioactive', 'very');
61 // test value retrieval
62 $this->assertIdentical($config->get('Element.Abbr'), 'Pu');
63 $this->assertIdentical($config->get('Element.Name'), 'plutonium');
64 $this->assertIdentical($config->get('Element.Number'), 94);
65 $this->assertIdentical($config->get('Element.Mass'), 244.);
66 $this->assertIdentical($config->get('Element.Radioactive'), true);
67 $this->assertIdentical($config->get('Element.Isotopes'), array(238 => true, 239 => true));
68 $this->assertIdentical($config->get('Element.Traits'), array('nuclear', 'heavy', 'actinide'));
69 $this->assertIdentical($config->get('Element.IsotopeNames'), array(238 => 'Plutonium-238', 239 => 'Plutonium-239'));
70 $this->assertIdentical($config->get('Element.Object'), false);
72 $this->expectError('Cannot retrieve value of undefined directive Element.Metal');
73 $config->get('Element.Metal');
77 public function testEnumerated()
79 // case sensitive
80 $this->schema->add('Instrument.Manufacturer', 'Yamaha', 'string', false);
81 $this->schema->addAllowedValues('Instrument.Manufacturer', array(
82 'Yamaha' => true, 'Conn-Selmer' => true, 'Vandoren' => true,
83 'Laubin' => true, 'Buffet' => true, 'other' => true));
84 $this->schema->addValueAliases('Instrument.Manufacturer', array(
85 'Selmer' => 'Conn-Selmer'));
87 // case insensitive
88 $this->schema->add('Instrument.Family', 'woodwind', 'istring', false);
89 $this->schema->addAllowedValues('Instrument.Family', array(
90 'brass' => true, 'woodwind' => true, 'percussion' => true,
91 'string' => true, 'keyboard' => true, 'electronic' => true));
92 $this->schema->addValueAliases('Instrument.Family', array(
93 'synth' => 'electronic'));
95 $config = new HTMLPurifier_Config($this->schema);
96 $config->autoFinalize = false;
97 $config->chatty = false;
99 // case sensitive
101 $config->set('Instrument.Manufacturer', 'Vandoren');
102 $this->assertIdentical($config->get('Instrument.Manufacturer'), 'Vandoren');
104 $config->set('Instrument.Manufacturer', 'Selmer');
105 $this->assertIdentical($config->get('Instrument.Manufacturer'), 'Conn-Selmer');
107 $this->expectError('Value not supported, valid values are: Yamaha, Conn-Selmer, Vandoren, Laubin, Buffet, other');
108 $config->set('Instrument.Manufacturer', 'buffet');
110 // case insensitive
112 $config->set('Instrument.Family', 'brass');
113 $this->assertIdentical($config->get('Instrument.Family'), 'brass');
115 $config->set('Instrument.Family', 'PERCUSSION');
116 $this->assertIdentical($config->get('Instrument.Family'), 'percussion');
118 $config->set('Instrument.Family', 'synth');
119 $this->assertIdentical($config->get('Instrument.Family'), 'electronic');
121 $config->set('Instrument.Family', 'Synth');
122 $this->assertIdentical($config->get('Instrument.Family'), 'electronic');
126 public function testNull()
128 $this->schema->add('ReportCard.English', null, 'string', true);
129 $this->schema->add('ReportCard.Absences', 0, 'int', false);
131 $config = new HTMLPurifier_Config($this->schema);
132 $config->autoFinalize = false;
133 $config->chatty = false;
135 $config->set('ReportCard.English', 'B-');
136 $this->assertIdentical($config->get('ReportCard.English'), 'B-');
138 $config->set('ReportCard.English', null); // not yet graded
139 $this->assertIdentical($config->get('ReportCard.English'), null);
141 // error
142 $this->expectError('Value for ReportCard.Absences is of invalid type, should be int');
143 $config->set('ReportCard.Absences', null);
147 public function testAliases()
149 $this->schema->add('Home.Rug', 3, 'int', false);
150 $this->schema->addAlias('Home.Carpet', 'Home.Rug');
152 $config = new HTMLPurifier_Config($this->schema);
153 $config->autoFinalize = false;
154 $config->chatty = false;
156 $this->assertIdentical($config->get('Home.Rug'), 3);
158 $this->expectError('Cannot get value from aliased directive, use real name Home.Rug');
159 $config->get('Home.Carpet');
161 $this->expectError('Home.Carpet is an alias, preferred directive name is Home.Rug');
162 $config->set('Home.Carpet', 999);
163 $this->assertIdentical($config->get('Home.Rug'), 999);
167 // test functionality based on method
169 public function test_getBatch()
171 $this->schema->add('Variables.TangentialAcceleration', 'a_tan', 'string', false);
172 $this->schema->add('Variables.AngularAcceleration', 'alpha', 'string', false);
174 $config = new HTMLPurifier_Config($this->schema);
175 $config->autoFinalize = false;
176 $config->chatty = false;
178 // grab a namespace
179 $this->assertIdentical(
180 $config->getBatch('Variables'),
181 array(
182 'TangentialAcceleration' => 'a_tan',
183 'AngularAcceleration' => 'alpha'
187 // grab a non-existant namespace
188 $this->expectError('Cannot retrieve undefined namespace Constants');
189 $config->getBatch('Constants');
193 public function test_loadIni()
195 $this->schema->add('Shortcut.Copy', 'c', 'istring', false);
196 $this->schema->add('Shortcut.Paste', 'v', 'istring', false);
197 $this->schema->add('Shortcut.Cut', 'x', 'istring', false);
199 $config = new HTMLPurifier_Config($this->schema);
200 $config->autoFinalize = false;
202 $config->loadIni(dirname(__FILE__) . '/ConfigTest-loadIni.ini');
204 $this->assertIdentical($config->get('Shortcut.Copy'), 'q');
205 $this->assertIdentical($config->get('Shortcut.Paste'), 'p');
206 $this->assertIdentical($config->get('Shortcut.Cut'), 't');
210 public function test_getHTMLDefinition()
212 // we actually want to use the old copy, because the definition
213 // generation routines have dependencies on configuration values
215 $config = HTMLPurifier_Config::createDefault();
216 $config->set('HTML.Doctype', 'XHTML 1.0 Strict');
217 $config->autoFinalize = false;
219 $def = $config->getCSSDefinition();
220 $this->assertIsA($def, 'HTMLPurifier_CSSDefinition');
222 $def = $config->getHTMLDefinition();
223 $def2 = $config->getHTMLDefinition();
224 $this->assertIsA($def, 'HTMLPurifier_HTMLDefinition');
225 $this->assertTrue($def === $def2);
226 $this->assertTrue($def->setup);
228 $old_def = clone $def2;
230 $config->set('HTML.Doctype', 'HTML 4.01 Transitional');
231 $def = $config->getHTMLDefinition();
232 $this->assertIsA($def, 'HTMLPurifier_HTMLDefinition');
233 $this->assertTrue($def !== $old_def);
234 $this->assertTrue($def->setup);
238 public function test_getHTMLDefinition_deprecatedRawError()
240 $config = HTMLPurifier_Config::createDefault();
241 $config->chatty = false;
242 // test deprecated retrieval of raw definition
243 $config->set('HTML.DefinitionID', 'HTMLPurifier_ConfigTest->test_getHTMLDefinition()');
244 $config->set('HTML.DefinitionRev', 3);
245 $this->expectError("Useless DefinitionID declaration");
246 $def = $config->getHTMLDefinition(true);
247 $this->assertEqual(false, $def->setup);
249 // auto initialization
250 $config->getHTMLDefinition();
251 $this->assertTrue($def->setup);
254 public function test_getHTMLDefinition_optimizedRawError()
256 $this->expectException(new HTMLPurifier_Exception("Cannot set optimized = true when raw = false"));
257 $config = HTMLPurifier_Config::createDefault();
258 $config->getHTMLDefinition(false, true);
261 public function test_getHTMLDefinition_rawAfterSetupError()
263 $this->expectException(new HTMLPurifier_Exception("Cannot retrieve raw definition after it has already been setup"));
264 $config = HTMLPurifier_Config::createDefault();
265 $config->chatty = false;
266 $config->getHTMLDefinition();
267 $config->getHTMLDefinition(true);
270 public function test_getHTMLDefinition_inconsistentOptimizedError()
272 $this->expectError("Useless DefinitionID declaration");
273 $this->expectException(new HTMLPurifier_Exception("Inconsistent use of optimized and unoptimized raw definition retrievals"));
274 $config = HTMLPurifier_Config::create(array('HTML.DefinitionID' => 'HTMLPurifier_ConfigTest->test_getHTMLDefinition_inconsistentOptimizedError'));
275 $config->chatty = false;
276 $config->getHTMLDefinition(true, false);
277 $config->getHTMLDefinition(true, true);
280 public function test_getHTMLDefinition_inconsistentOptimizedError2()
282 $this->expectException(new HTMLPurifier_Exception("Inconsistent use of optimized and unoptimized raw definition retrievals"));
283 $config = HTMLPurifier_Config::create(array('HTML.DefinitionID' => 'HTMLPurifier_ConfigTest->test_getHTMLDefinition_inconsistentOptimizedError2'));
284 $config->chatty = false;
285 $config->getHTMLDefinition(true, true);
286 $config->getHTMLDefinition(true, false);
289 public function test_getHTMLDefinition_rawError()
291 $config = HTMLPurifier_Config::createDefault();
292 $this->expectException(new HTMLPurifier_Exception('Cannot retrieve raw version without specifying %HTML.DefinitionID'));
293 $def = $config->getHTMLDefinition(true, true);
296 public function test_getCSSDefinition()
298 $config = HTMLPurifier_Config::createDefault();
299 $def = $config->getCSSDefinition();
300 $this->assertIsA($def, 'HTMLPurifier_CSSDefinition');
303 public function test_getDefinition()
305 $this->schema->add('Cache.DefinitionImpl', null, 'string', true);
306 $config = new HTMLPurifier_Config($this->schema);
307 $this->expectException(new HTMLPurifier_Exception("Definition of Crust type not supported"));
308 $config->getDefinition('Crust');
311 public function test_loadArray()
313 // setup a few dummy namespaces/directives for our testing
314 $this->schema->add('Zoo.Aadvark', 0, 'int', false);
315 $this->schema->add('Zoo.Boar', 0, 'int', false);
316 $this->schema->add('Zoo.Camel', 0, 'int', false);
317 $this->schema->add('Zoo.Others', array(), 'list', false);
319 $config_manual = new HTMLPurifier_Config($this->schema);
320 $config_loadabbr = new HTMLPurifier_Config($this->schema);
321 $config_loadfull = new HTMLPurifier_Config($this->schema);
323 $config_manual->set('Zoo.Aadvark', 3);
324 $config_manual->set('Zoo.Boar', 5);
325 $config_manual->set('Zoo.Camel', 2000); // that's a lotta camels!
326 $config_manual->set('Zoo.Others', array('Peacock', 'Dodo')); // wtf!
328 // condensed form
329 $config_loadabbr->loadArray(array(
330 'Zoo.Aadvark' => 3,
331 'Zoo.Boar' => 5,
332 'Zoo.Camel' => 2000,
333 'Zoo.Others' => array('Peacock', 'Dodo')
336 // fully expanded form
337 $config_loadfull->loadArray(array(
338 'Zoo' => array(
339 'Aadvark' => 3,
340 'Boar' => 5,
341 'Camel' => 2000,
342 'Others' => array('Peacock', 'Dodo')
346 $this->assertIdentical($config_manual, $config_loadabbr);
347 $this->assertIdentical($config_manual, $config_loadfull);
351 public function test_create()
353 $this->schema->add('Cake.Sprinkles', 666, 'int', false);
354 $this->schema->add('Cake.Flavor', 'vanilla', 'string', false);
356 $config = new HTMLPurifier_Config($this->schema);
357 $config->set('Cake.Sprinkles', 42);
359 // test flat pass-through
360 $created_config = HTMLPurifier_Config::create($config, $this->schema);
361 $this->assertIdentical($config, $created_config);
363 // test loadArray
364 $created_config = HTMLPurifier_Config::create(array('Cake.Sprinkles' => 42), $this->schema);
365 $this->assertIdentical($config, $created_config);
367 // test loadIni
368 $created_config = HTMLPurifier_Config::create(dirname(__FILE__) . '/ConfigTest-create.ini', $this->schema);
369 $this->assertIdentical($config, $created_config);
373 public function test_finalize()
375 // test finalization
377 $this->schema->add('Poem.Meter', 'iambic', 'string', false);
379 $config = new HTMLPurifier_Config($this->schema);
380 $config->autoFinalize = false;
381 $config->chatty = false;
383 $config->set('Poem.Meter', 'irregular');
385 $config->finalize();
387 $this->expectError('Cannot set directive after finalization');
388 $config->set('Poem.Meter', 'vedic');
390 $this->expectError('Cannot load directives after finalization');
391 $config->loadArray(array('Poem.Meter' => 'octosyllable'));
393 $this->expectError('Cannot load directives after finalization');
394 $config->loadIni(dirname(__FILE__) . '/ConfigTest-finalize.ini');
398 public function test_loadArrayFromForm()
400 $this->schema->add('Pancake.Mix', 'buttermilk', 'string', false);
401 $this->schema->add('Pancake.Served', true, 'bool', false);
402 $this->schema->add('Toppings.Syrup', true, 'bool', false);
403 $this->schema->add('Toppings.Flavor', 'maple', 'string', false);
404 $this->schema->add('Toppings.Strawberries', 3, 'int', false);
405 $this->schema->add('Toppings.Calories', 2000, 'int', true);
406 $this->schema->add('Toppings.DefinitionID', null, 'string', true);
407 $this->schema->add('Toppings.DefinitionRev', 1, 'int', false);
408 $this->schema->add('Toppings.Protected', 1, 'int', false);
410 $get = array(
411 'breakfast' => array(
412 'Pancake.Mix' => 'nasty',
413 'Pancake.Served' => '0',
414 'Toppings.Syrup' => '0',
415 'Toppings.Flavor' => "juice",
416 'Toppings.Strawberries' => '999',
417 'Toppings.Calories' => '',
418 'Null_Toppings.Calories' => '1',
419 'Toppings.DefinitionID' => '<argh>',
420 'Toppings.DefinitionRev' => '65',
421 'Toppings.Protected' => '4',
425 $config_expect = HTMLPurifier_Config::create(array(
426 'Pancake.Served' => false,
427 'Toppings.Syrup' => false,
428 'Toppings.Flavor' => "juice",
429 'Toppings.Strawberries' => 999,
430 'Toppings.Calories' => null
431 ), $this->schema);
433 $config_result = HTMLPurifier_Config::loadArrayFromForm(
434 $get, 'breakfast',
435 array('Pancake.Served', 'Toppings', '-Toppings.Protected'),
436 false, // mq fix
437 $this->schema
440 $this->assertEqual($config_expect, $config_result);
443 MAGIC QUOTES NOT TESTED!!!
445 $get = array(
446 'breakfast' => array(
447 'Pancake.Mix' => 'n\\asty'
450 $config_expect = HTMLPurifier_Config::create(array(
451 'Pancake.Mix' => 'n\\asty'
453 $config_result = HTMLPurifier_Config::loadArrayFromForm($get, 'breakfast', true, false);
454 $this->assertEqual($config_expect, $config_result);
458 public function test_getAllowedDirectivesForForm()
460 $this->schema->add('Unused.Unused', 'Foobar', 'string', false);
461 $this->schema->add('Partial.Allowed', true, 'bool', false);
462 $this->schema->add('Partial.Unused', 'Foobar', 'string', false);
463 $this->schema->add('All.Allowed', true, 'bool', false);
464 $this->schema->add('All.Blacklisted', 'Foobar', 'string', false); // explicitly blacklisted
465 $this->schema->add('All.DefinitionID', 'Foobar', 'string', true); // auto-blacklisted
466 $this->schema->add('All.DefinitionRev', 2, 'int', false); // auto-blacklisted
468 $input = array('Partial.Allowed', 'All', '-All.Blacklisted');
469 $output = HTMLPurifier_Config::getAllowedDirectivesForForm($input, $this->schema);
470 $expect = array(
471 array('Partial', 'Allowed'),
472 array('All', 'Allowed')
475 $this->assertEqual($output, $expect);
479 public function testDeprecatedAPI()
481 $this->schema->add('Foo.Bar', 2, 'int', false);
482 $config = new HTMLPurifier_Config($this->schema);
483 $config->chatty = false;
484 $this->expectError('Using deprecated API: use $config->set(\'Foo.Bar\', ...) instead');
485 $config->set('Foo', 'Bar', 4);
486 $this->expectError('Using deprecated API: use $config->get(\'Foo.Bar\') instead');
487 $this->assertIdentical($config->get('Foo', 'Bar'), 4);
490 public function testInherit()
492 $this->schema->add('Phantom.Masked', 25, 'int', false);
493 $this->schema->add('Phantom.Unmasked', 89, 'int', false);
494 $this->schema->add('Phantom.Latemasked', 11, 'int', false);
495 $config = new HTMLPurifier_Config($this->schema);
496 $config->set('Phantom.Masked', 800);
497 $subconfig = HTMLPurifier_Config::inherit($config);
498 $config->set('Phantom.Latemasked', 100, 'int', false);
499 $this->assertIdentical($subconfig->get('Phantom.Masked'), 800);
500 $this->assertIdentical($subconfig->get('Phantom.Unmasked'), 89);
501 $this->assertIdentical($subconfig->get('Phantom.Latemasked'), 100);
504 public function testSerialize()
506 $config = HTMLPurifier_Config::createDefault();
507 $config->set('HTML.Allowed', 'a');
508 $config2 = unserialize($config->serialize());
509 $this->assertIdentical($config->get('HTML.Allowed'), $config2->get('HTML.Allowed'));
512 public function testDefinitionCachingNothing()
514 list($mock, $config) = $this->setupCacheMock('HTML');
515 // should not touch the cache
516 $mock->expectNever('get');
517 $mock->expectNever('add');
518 $mock->expectNever('set');
519 $config->getDefinition('HTML', true);
520 $config->getDefinition('HTML', true);
521 $config->getDefinition('HTML');
522 $this->teardownCacheMock();
525 public function testDefinitionCachingOptimized()
527 list($mock, $config) = $this->setupCacheMock('HTML');
528 $mock->expectNever('set');
529 $config->set('HTML.DefinitionID', 'HTMLPurifier_ConfigTest->testDefinitionCachingOptimized');
530 $mock->expectOnce('get');
531 $mock->returns('get', null);
532 $this->assertTrue($config->maybeGetRawHTMLDefinition());
533 $this->assertTrue($config->maybeGetRawHTMLDefinition());
534 $mock->expectOnce('add');
535 $config->getDefinition('HTML');
536 $this->teardownCacheMock();
539 public function testDefinitionCachingOptimizedHit()
541 $fake_config = HTMLPurifier_Config::createDefault();
542 $fake_def = $fake_config->getHTMLDefinition();
543 list($mock, $config) = $this->setupCacheMock('HTML');
544 // should never frob cache
545 $mock->expectNever('add');
546 $mock->expectNever('set');
547 $config->set('HTML.DefinitionID', 'HTMLPurifier_ConfigTest->testDefinitionCachingOptimizedHit');
548 $mock->expectOnce('get');
549 $mock->returns('get', $fake_def);
550 $this->assertNull($config->maybeGetRawHTMLDefinition());
551 $config->getDefinition('HTML');
552 $config->getDefinition('HTML');
553 $this->teardownCacheMock();
556 protected function setupCacheMock($type)
558 // inject our definition cache mock globally (borrowed from
559 // DefinitionFactoryTest)
560 generate_mock_once("HTMLPurifier_DefinitionCacheFactory");
561 $factory = new HTMLPurifier_DefinitionCacheFactoryMock();
562 $this->oldFactory = HTMLPurifier_DefinitionCacheFactory::instance();
563 HTMLPurifier_DefinitionCacheFactory::instance($factory);
564 generate_mock_once("HTMLPurifier_DefinitionCache");
565 $mock = new HTMLPurifier_DefinitionCacheMock();
566 $config = HTMLPurifier_Config::createDefault();
567 $factory->returns('create', $mock, array($type, $config));
568 return array($mock, $config);
570 protected function teardownCacheMock()
572 HTMLPurifier_DefinitionCacheFactory::instance($this->oldFactory);
577 // vim: et sw=4 sts=4