diff --git a/library/HTMLPurifier/ConfigSchema/Interchange.php b/library/HTMLPurifier/ConfigSchema/Interchange.php
index 1b1a2489..18866dbf 100644
--- a/library/HTMLPurifier/ConfigSchema/Interchange.php
+++ b/library/HTMLPurifier/ConfigSchema/Interchange.php
@@ -22,14 +22,20 @@ class HTMLPurifier_ConfigSchema_Interchange
* Adds a namespace array to $namespaces
*/
public function addNamespace($namespace) {
- $this->namespaces[$namespace->namespace] = $namespace;
+ if (isset($this->namespaces[$i = $namespace->namespace])) {
+ throw new HTMLPurifier_ConfigSchema_Exception("Cannot redefine namespace '$i'");
+ }
+ $this->namespaces[$i] = $namespace;
}
/**
* Adds a directive array to $directives
*/
public function addDirective($directive) {
- $this->directives[$directive->id->__toString()] = $directive;
+ if (isset($this->directives[$i = $directive->id->__toString()])) {
+ throw new HTMLPurifier_ConfigSchema_Exception("Cannot redefine directive '$i'");
+ }
+ $this->directives[$i] = $directive;
}
}
diff --git a/library/HTMLPurifier/ConfigSchema/InterchangeBuilder.php b/library/HTMLPurifier/ConfigSchema/InterchangeBuilder.php
index 6020e573..f010cdfc 100644
--- a/library/HTMLPurifier/ConfigSchema/InterchangeBuilder.php
+++ b/library/HTMLPurifier/ConfigSchema/InterchangeBuilder.php
@@ -15,6 +15,12 @@ class HTMLPurifier_ConfigSchema_InterchangeBuilder
* @param $hash HTMLPurifier_ConfigSchema_StringHash source data
*/
public function build($interchange, $hash) {
+ if (!$hash instanceof HTMLPurifier_StringHash) {
+ $hash = new HTMLPurifier_StringHash($hash);
+ }
+ if (!isset($hash['ID'])) {
+ throw new HTMLPurifier_ConfigSchema_Exception('Hash does not have any ID');
+ }
if (strpos($hash['ID'], '.') === false) {
$this->buildNamespace($interchange, $hash);
} else {
@@ -26,7 +32,9 @@ class HTMLPurifier_ConfigSchema_InterchangeBuilder
public function buildNamespace($interchange, $hash) {
$namespace = new HTMLPurifier_ConfigSchema_Interchange_Namespace();
$namespace->namespace = $hash->offsetGet('ID');
- $namespace->description = $hash->offsetGet('DESCRIPTION');
+ if (isset($hash['DESCRIPTION'])) {
+ $namespace->description = $hash->offsetGet('DESCRIPTION');
+ }
$interchange->addNamespace($namespace);
}
@@ -35,33 +43,43 @@ class HTMLPurifier_ConfigSchema_InterchangeBuilder
// These are required elements:
$directive->id = $this->id($hash->offsetGet('ID'));
- $type = explode('/', $hash->offsetGet('TYPE'));
- if (isset($type[1])) $directive->typeAllowsNull = true;
- $directive->type = $type[0];
- $directive->description = $directive->offsetGet('DESCRIPTION');
- // These are extras:
- if (isset($directive['ALLOWED'])) {
- $directive->allowed = $this->lookup($this->evalArray($directive->offsetGet('ALLOWED')));
+ if (isset($hash['TYPE'])) {
+ $type = explode('/', $hash->offsetGet('TYPE'));
+ if (isset($type[1])) $directive->typeAllowsNull = true;
+ $directive->type = $type[0];
}
- if (isset($directive['VALUE-ALIASES'])) {
- $directive->valueAliases = $this->evalArray($directive->offsetGet('VALUE-ALIASES'));
+
+ if (isset($hash['DESCRIPTION'])) {
+ $directive->description = $hash->offsetGet('DESCRIPTION');
}
- if (isset($directive['ALIASES'])) {
+
+ if (isset($hash['ALLOWED'])) {
+ $directive->allowed = $this->lookup($this->evalArray($hash->offsetGet('ALLOWED')));
+ }
+
+ if (isset($hash['VALUE-ALIASES'])) {
+ $directive->valueAliases = $this->evalArray($hash->offsetGet('VALUE-ALIASES'));
+ }
+
+ if (isset($hash['ALIASES'])) {
$raw_aliases = trim($hash->offsetGet('ALIASES'));
$aliases = preg_split('/\s*,\s*/', $raw_aliases);
foreach ($aliases as $alias) {
- $this->aliases[] = $this->id($alias);
+ $directive->aliases[] = $this->id($alias);
}
}
- if (isset($directive['VERSION'])) {
- $directive->version = $directive->offsetGet('VERSION');
+
+ if (isset($hash['VERSION'])) {
+ $directive->version = $hash->offsetGet('VERSION');
}
- if (isset($directive['DEPRECATED-USE'])) {
- $directive->deprecatedUse = $this->id($directive->offsetGet('DEPRECATED-USE'));
+
+ if (isset($hash['DEPRECATED-USE'])) {
+ $directive->deprecatedUse = $this->id($hash->offsetGet('DEPRECATED-USE'));
}
- if (isset($directive['DEPRECATED-VERSION'])) {
- $directive->deprecatedVersion = $directive->offsetGet('DEPRECATED-VERSION');
+
+ if (isset($hash['DEPRECATED-VERSION'])) {
+ $directive->deprecatedVersion = $hash->offsetGet('DEPRECATED-VERSION');
}
$interchange->addDirective($directive);
diff --git a/library/HTMLPurifier/ConfigSchema/Validator.php b/library/HTMLPurifier/ConfigSchema/Validator.php
index 48c0f82a..88125877 100644
--- a/library/HTMLPurifier/ConfigSchema/Validator.php
+++ b/library/HTMLPurifier/ConfigSchema/Validator.php
@@ -11,7 +11,7 @@ class HTMLPurifier_ConfigSchema_Validator
/**
* Volatile context variables to provide a fluent interface.
*/
- protected $context, $obj, $member;
+ protected $context = array(), $obj, $member;
/**
* Validates a fully-formed interchange object. Throws an
@@ -28,38 +28,53 @@ class HTMLPurifier_ConfigSchema_Validator
}
public function validateNamespace($n) {
- $this->context = "namespace '{$n->namespace}'";
+ $this->context[] = "namespace '{$n->namespace}'";
$this->with($n, 'namespace')
->assertNotEmpty()
->assertAlnum();
$this->with($n, 'description')
- ->assertIsString()
- ->assertNotEmpty();
+ ->assertNotEmpty()
+ ->assertIsString(); // technically redundant
+ array_pop($this->context);
}
public function validateDirective($d) {
- $this->context = "directive '{$d->id}'";
+ $this->context[] = "directive '{$d->id}'";
$this->validateId($d->id);
+ $this->with($d, 'description')
+ ->assertNotEmpty();
+ if (!isset(HTMLPurifier_VarParser::$types[$d->type])) {
+ $this->error('type', 'is invalid');
+ }
+ array_pop($this->context);
}
public function validateId($id) {
- $this->context = "id '$id'";
+ $this->context[] = "id '$id'";
+ if (!isset($this->interchange->namespaces[$id->namespace])) {
+ $this->error('namespace', 'does not exist');
+ }
$this->with($id, 'namespace')
->assertNotEmpty()
->assertAlnum();
$this->with($id, 'directive')
->assertNotEmpty()
->assertAlnum();
+ array_pop($this->context);
}
// protected helper functions
protected function with($obj, $member) {
- return new HTMLPurifier_ConfigSchema_ValidatorAtom($this->context, $obj, $member);
+ return new HTMLPurifier_ConfigSchema_ValidatorAtom($this->getFormattedContext(), $obj, $member);
}
- protected function error($msg) {
- throw new HTMLPurifier_ConfigSchema_Exception($msg . ' in ' . $this->context);
+ protected function error($target, $msg) {
+ throw new HTMLPurifier_ConfigSchema_Exception(ucfirst($target) . ' in ' . $this->getFormattedContext() . ' ' . $msg);
+ }
+
+ protected function getFormattedContext() {
+ return implode(' in ', array_reverse($this->context));
}
}
diff --git a/library/HTMLPurifier/ConfigSchema/ValidatorAtom.php b/library/HTMLPurifier/ConfigSchema/ValidatorAtom.php
index b799d67b..ac8d3e5d 100644
--- a/library/HTMLPurifier/ConfigSchema/ValidatorAtom.php
+++ b/library/HTMLPurifier/ConfigSchema/ValidatorAtom.php
@@ -40,7 +40,7 @@ class HTMLPurifier_ConfigSchema_ValidatorAtom
}
protected function error($msg) {
- throw new HTMLPurifier_ConfigSchema_Exception('Member variable \'' . $this->member . '\' in ' . $this->context . ' ' . $msg);
+ throw new HTMLPurifier_ConfigSchema_Exception(ucfirst($this->member) . ' in ' . $this->context . ' ' . $msg);
}
}
diff --git a/tests/HTMLPurifier/ConfigSchema/Validator/directive/descriptionNotEmpty.vtest b/tests/HTMLPurifier/ConfigSchema/Validator/directive/descriptionNotEmpty.vtest
new file mode 100644
index 00000000..fd2d0922
--- /dev/null
+++ b/tests/HTMLPurifier/ConfigSchema/Validator/directive/descriptionNotEmpty.vtest
@@ -0,0 +1,7 @@
+ERROR: Description in directive 'Ns.Dir' must not be empty
+----
+Ns
+DESCRIPTION: Our namespace.
+----
+Ns.Dir
+TYPE: int
diff --git a/tests/HTMLPurifier/ConfigSchema/Validator/directive/idDirectiveAlnum.vtest b/tests/HTMLPurifier/ConfigSchema/Validator/directive/idDirectiveAlnum.vtest
new file mode 100644
index 00000000..9ffd143b
--- /dev/null
+++ b/tests/HTMLPurifier/ConfigSchema/Validator/directive/idDirectiveAlnum.vtest
@@ -0,0 +1,8 @@
+ERROR: Directive in id 'Ns.+' in directive 'Ns.+' must be alphanumeric
+----
+Ns
+DESCRIPTION: Generic namespace.
+----
+ID: Ns.+
+TYPE: int
+DESCRIPTION: Description
diff --git a/tests/HTMLPurifier/ConfigSchema/Validator/directive/idDirectiveNotEmpty.vtest b/tests/HTMLPurifier/ConfigSchema/Validator/directive/idDirectiveNotEmpty.vtest
new file mode 100644
index 00000000..27e11282
--- /dev/null
+++ b/tests/HTMLPurifier/ConfigSchema/Validator/directive/idDirectiveNotEmpty.vtest
@@ -0,0 +1,8 @@
+ERROR: Directive in id 'Ns.' in directive 'Ns.' must not be empty
+----
+Ns
+DESCRIPTION: Our namespace
+----
+ID: Ns.
+TYPE: int
+DESCRIPTION: Description.
diff --git a/tests/HTMLPurifier/ConfigSchema/Validator/directive/idNamespaceExists.vtest b/tests/HTMLPurifier/ConfigSchema/Validator/directive/idNamespaceExists.vtest
new file mode 100644
index 00000000..b216bf8d
--- /dev/null
+++ b/tests/HTMLPurifier/ConfigSchema/Validator/directive/idNamespaceExists.vtest
@@ -0,0 +1,5 @@
+ERROR: Namespace in id 'Rd.Dir' in directive 'Rd.Dir' does not exist
+----
+ID: Rd.Dir
+TYPE: int
+DESCRIPTION: Description
diff --git a/tests/HTMLPurifier/ConfigSchema/Validator/directive/typeExists.vtest b/tests/HTMLPurifier/ConfigSchema/Validator/directive/typeExists.vtest
new file mode 100644
index 00000000..dd2ae907
--- /dev/null
+++ b/tests/HTMLPurifier/ConfigSchema/Validator/directive/typeExists.vtest
@@ -0,0 +1,8 @@
+ERROR: Type in directive 'Ns.Dir' is invalid
+----
+Ns
+DESCRIPTION: Namespace
+----
+Ns.Dir
+DESCRIPTION: Directive
+TYPE: foobar
diff --git a/tests/HTMLPurifier/ConfigSchema/Validator/directive/unique.vtest b/tests/HTMLPurifier/ConfigSchema/Validator/directive/unique.vtest
new file mode 100644
index 00000000..7fbd4744
--- /dev/null
+++ b/tests/HTMLPurifier/ConfigSchema/Validator/directive/unique.vtest
@@ -0,0 +1,9 @@
+ERROR: Cannot redefine directive 'Ns.Dir'
+----
+ID: Ns.Dir
+DESCRIPTION: Version 1
+TYPE: int
+----
+ID: Ns.Dir
+DESCRIPTION: Version 2
+TYPE: int
diff --git a/tests/HTMLPurifier/ConfigSchema/Validator/namespace/descriptionNotEmpty.vtest b/tests/HTMLPurifier/ConfigSchema/Validator/namespace/descriptionNotEmpty.vtest
new file mode 100644
index 00000000..888218a0
--- /dev/null
+++ b/tests/HTMLPurifier/ConfigSchema/Validator/namespace/descriptionNotEmpty.vtest
@@ -0,0 +1,3 @@
+ERROR: Description in namespace 'Ns' must not be empty
+----
+ID: Ns
diff --git a/tests/HTMLPurifier/ConfigSchema/Validator/namespace/namespaceAlnum.vtest b/tests/HTMLPurifier/ConfigSchema/Validator/namespace/namespaceAlnum.vtest
new file mode 100644
index 00000000..5b914f75
--- /dev/null
+++ b/tests/HTMLPurifier/ConfigSchema/Validator/namespace/namespaceAlnum.vtest
@@ -0,0 +1,4 @@
+ERROR: Namespace in namespace 'R&D' must be alphanumeric
+----
+ID: R&D
+DESCRIPTION: ctype_alnum($ID) === false
diff --git a/tests/HTMLPurifier/ConfigSchema/Validator/namespace/namespaceNotEmpty.vtest b/tests/HTMLPurifier/ConfigSchema/Validator/namespace/namespaceNotEmpty.vtest
new file mode 100644
index 00000000..42ba5baa
--- /dev/null
+++ b/tests/HTMLPurifier/ConfigSchema/Validator/namespace/namespaceNotEmpty.vtest
@@ -0,0 +1,4 @@
+ERROR: Namespace in namespace '0' must not be empty
+----
+ID: 0
+DESCRIPTION: empty($ID) === true
diff --git a/tests/HTMLPurifier/ConfigSchema/Validator/namespace/normal.vtest b/tests/HTMLPurifier/ConfigSchema/Validator/namespace/normal.vtest
new file mode 100644
index 00000000..13238143
--- /dev/null
+++ b/tests/HTMLPurifier/ConfigSchema/Validator/namespace/normal.vtest
@@ -0,0 +1,2 @@
+Namespace
+DESCRIPTION: This is a generic namespace.
diff --git a/tests/HTMLPurifier/ConfigSchema/Validator/namespace/unique.vtest b/tests/HTMLPurifier/ConfigSchema/Validator/namespace/unique.vtest
new file mode 100644
index 00000000..1d1f12d1
--- /dev/null
+++ b/tests/HTMLPurifier/ConfigSchema/Validator/namespace/unique.vtest
@@ -0,0 +1,7 @@
+ERROR: Cannot redefine namespace 'Ns'
+----
+ID: Ns
+DESCRIPTION: Version 1
+----
+ID: Ns
+DESCRIPTION: Version 2
diff --git a/tests/HTMLPurifier/ConfigSchema/ValidatorAtomTest.php b/tests/HTMLPurifier/ConfigSchema/ValidatorAtomTest.php
index 7dc027f9..fe6cc262 100644
--- a/tests/HTMLPurifier/ConfigSchema/ValidatorAtomTest.php
+++ b/tests/HTMLPurifier/ConfigSchema/ValidatorAtomTest.php
@@ -19,7 +19,7 @@ class HTMLPurifier_ConfigSchema_ValidatorAtomTest extends UnitTestCase
}
public function testAssertIsStringFail() {
- $this->expectValidationException("Member variable 'property' in context must be a string");
+ $this->expectValidationException("Property in context must be a string");
$this->makeAtom(3)->assertIsString();
}
@@ -28,7 +28,7 @@ class HTMLPurifier_ConfigSchema_ValidatorAtomTest extends UnitTestCase
}
public function testAssertNotNullFail() {
- $this->expectValidationException("Member variable 'property' in context must not be null");
+ $this->expectValidationException("Property in context must not be null");
$this->makeAtom(null)->assertNotNull();
}
@@ -37,12 +37,12 @@ class HTMLPurifier_ConfigSchema_ValidatorAtomTest extends UnitTestCase
}
public function testAssertAlnumFail() {
- $this->expectValidationException("Member variable 'property' in context must be alphanumeric");
+ $this->expectValidationException("Property in context must be alphanumeric");
$this->makeAtom('%a')->assertAlnum();
}
public function testAssertAlnumFailIsString() {
- $this->expectValidationException("Member variable 'property' in context must be a string");
+ $this->expectValidationException("Property in context must be a string");
$this->makeAtom(3)->assertAlnum();
}
@@ -51,7 +51,7 @@ class HTMLPurifier_ConfigSchema_ValidatorAtomTest extends UnitTestCase
}
public function testAssertNotEmptyFail() {
- $this->expectValidationException("Member variable 'property' in context must not be empty");
+ $this->expectValidationException("Property in context must not be empty");
$this->makeAtom('')->assertNotEmpty();
}
diff --git a/tests/HTMLPurifier/ConfigSchema/ValidatorTest.php b/tests/HTMLPurifier/ConfigSchema/ValidatorTest.php
deleted file mode 100644
index 11404b38..00000000
--- a/tests/HTMLPurifier/ConfigSchema/ValidatorTest.php
+++ /dev/null
@@ -1,75 +0,0 @@
-interchange = new HTMLPurifier_ConfigSchema_Interchange();
- $this->validator = new HTMLPurifier_ConfigSchema_Validator();
- }
-
- /**
- * Adds a namespace to our interchange object.
- */
- protected function addNamespace($namespace, $description) {
- $obj = new HTMLPurifier_ConfigSchema_Interchange_Namespace();
- $obj->namespace = $namespace;
- $obj->description = $description;
- $this->interchange->addNamespace($obj);
- return $obj;
- }
-
- protected function addDirective($namespace, $directive, $type = 'string', $description = 'Description') {
- $obj = new HTMLPurifier_ConfigSchema_Interchange_Directive();
- $obj->id = $this->makeId($namespace, $directive);
- $obj->type = $type;
- $obj->description = $description;
- $this->interchange->addDirective($obj);
- return $obj; // for future editing
- }
-
- protected function makeId($namespace, $directive) {
- return new HTMLPurifier_ConfigSchema_Interchange_Id($namespace, $directive);
- }
-
- /**
- * Invokes a validation, so we can fish for exceptions
- */
- protected function validate() {
- $this->validator->validate($this->interchange);
- }
-
- protected function expectValidationException($msg) {
- $this->expectException(new HTMLPurifier_ConfigSchema_Exception($msg));
- }
-
- public function testNamespace() {
- $this->addNamespace('Namespace', 'This is a generic namespace');
- $this->validate();
- }
-
- public function testNamespaceNamespaceNotEmpty() {
- $this->addNamespace('0', 'Description');
- $this->expectValidationException("Member variable 'namespace' in namespace '0' must not be empty");
- $this->validate();
- }
-
- public function testNamespaceNamespaceAlnum() {
- $this->addNamespace('%', 'Description');
- $this->expectValidationException("Member variable 'namespace' in namespace '%' must be alphanumeric");
- $this->validate();
- }
-
- public function testNamespaceDescriptionNotEmpty() {
- $this->addNamespace('Ns', '');
- $this->expectValidationException("Member variable 'description' in namespace 'Ns' must not be empty");
- $this->validate();
- }
-
- public function testDirectiveIdNamespaceNotEmpty() {
- $this->addDirective('', 'Dir');
- }
-
-}
diff --git a/tests/HTMLPurifier/ConfigSchema/ValidatorTestCase.php b/tests/HTMLPurifier/ConfigSchema/ValidatorTestCase.php
new file mode 100644
index 00000000..eb016eed
--- /dev/null
+++ b/tests/HTMLPurifier/ConfigSchema/ValidatorTestCase.php
@@ -0,0 +1,42 @@
+_path = $path;
+ $this->_parser = new HTMLPurifier_StringHashParser();
+ $this->_builder = new HTMLPurifier_ConfigSchema_InterchangeBuilder();
+ parent::UnitTestCase($path);
+ }
+
+ public function setup() {
+ $this->validator = new HTMLPurifier_ConfigSchema_Validator();
+ }
+
+ public function testValidator() {
+ $hashes = $this->_parser->parseMultiFile($this->_path);
+ $interchange = new HTMLPurifier_ConfigSchema_Interchange();
+ $error = null;
+ foreach ($hashes as $hash) {
+ if (!isset($hash['ID'])) {
+ if (isset($hash['ERROR'])) {
+ $this->expectException(
+ new HTMLPurifier_ConfigSchema_Exception($hash['ERROR'])
+ );
+ }
+ continue;
+ }
+ $this->_builder->build($interchange, new HTMLPurifier_StringHash($hash));
+ }
+ $this->validator->validate($interchange);
+ $this->pass();
+ }
+
+}
diff --git a/tests/common.php b/tests/common.php
index 31820d19..9fc987ff 100644
--- a/tests/common.php
+++ b/tests/common.php
@@ -156,6 +156,8 @@ function htmlpurifier_add_test($test, $test_file, $only_phpt = false) {
case '.php':
require_once $test_file;
return $test->addTestClass(path2class($test_file));
+ case '.vtest':
+ return $test->addTestCase(new HTMLPurifier_ConfigSchema_ValidatorTestCase($test_file));
default:
trigger_error("$test_file is an invalid file for testing", E_USER_ERROR);
}
diff --git a/tests/index.php b/tests/index.php
index b045b804..cfd6bf4c 100755
--- a/tests/index.php
+++ b/tests/index.php
@@ -110,6 +110,14 @@ foreach ($test_dirs as $dir) {
}
}
+// handle vtest dirs
+foreach ($vtest_dirs as $dir) {
+ $raw_files = $FS->globr($dir, '*.vtest');
+ foreach ($raw_files as $file) {
+ $test_files[] = str_replace('\\', '/', $file);
+ }
+}
+
// handle phpt files
foreach ($phpt_dirs as $dir) {
$phpt_files = $FS->globr($dir, '*.phpt');
diff --git a/tests/test_files.php b/tests/test_files.php
index 210112e2..def93e22 100644
--- a/tests/test_files.php
+++ b/tests/test_files.php
@@ -15,6 +15,10 @@ if (!$AC['only-phpt']) {
$test_files[] = 'HTMLPurifier/Filter/ExtractStyleBlocksTest.php';
}
+ // ConfigSchema Validator tests
+ $vtest_dirs = array();
+ $vtest_dirs[] = 'HTMLPurifier/ConfigSchema/Validator';
+
// ConfigDoc auxiliary library
if (version_compare(PHP_VERSION, '5.2', '>=')) {
$test_dirs[] = 'ConfigDoc';