diff options
Diffstat (limited to 'src/lxml/tests/test_isoschematron.py')
-rw-r--r-- | src/lxml/tests/test_isoschematron.py | 776 |
1 files changed, 776 insertions, 0 deletions
diff --git a/src/lxml/tests/test_isoschematron.py b/src/lxml/tests/test_isoschematron.py new file mode 100644 index 00000000..dcdcdb50 --- /dev/null +++ b/src/lxml/tests/test_isoschematron.py @@ -0,0 +1,776 @@ +# -*- coding: utf-8 -*- + +""" +Test cases related to ISO-Schematron parsing and validation +""" + +import unittest, sys, os.path +from lxml import isoschematron + +this_dir = os.path.dirname(__file__) +if this_dir not in sys.path: + sys.path.insert(0, this_dir) # needed for Py3 + +from common_imports import etree, HelperTestCase, fileInTestDir +from common_imports import doctest, make_doctest + +class ETreeISOSchematronTestCase(HelperTestCase): + def test_schematron(self): + tree_valid = self.parse('<AAA><BBB/><CCC/></AAA>') + tree_invalid = self.parse('<AAA><BBB/><CCC/><DDD/></AAA>') + schema = self.parse('''\ +<schema xmlns="http://purl.oclc.org/dsdl/schematron" > + <pattern id="OpenModel"> + <title>Open Model</title> + <rule context="AAA"> + <assert test="BBB"> BBB element is not present</assert> + <assert test="CCC"> CCC element is not present</assert> + </rule> + </pattern> + <pattern id="ClosedModel"> + <title>Closed model"</title> + <rule context="AAA"> + <assert test="BBB"> BBB element is not present</assert> + <assert test="CCC"> CCC element is not present</assert> + <assert test="count(BBB|CCC) = count (*)">There is an extra element</assert> + </rule> + </pattern> +</schema> +''') + schema = isoschematron.Schematron(schema) + self.assert_(schema.validate(tree_valid)) + self.assert_(not schema.validate(tree_invalid)) + + def test_schematron_elementtree_error(self): + self.assertRaises(ValueError, isoschematron.Schematron, etree.ElementTree()) + + # an empty pattern is valid in iso schematron + def test_schematron_empty_pattern(self): + schema = self.parse('''\ +<schema xmlns="http://purl.oclc.org/dsdl/schematron" > + <pattern id="OpenModel"> + <title>Open model</title> + </pattern> +</schema> +''') + schema = isoschematron.Schematron(schema) + self.assert_(schema) + + def test_schematron_invalid_schema_empty(self): + schema = self.parse('''\ +<schema xmlns="http://purl.oclc.org/dsdl/schematron" /> +''') + self.assertRaises(etree.SchematronParseError, + isoschematron.Schematron, schema) + + def test_schematron_invalid_schema_namespace(self): + schema = self.parse('''\ +<schema xmlns="mynamespace" /> +''') + self.assertRaises(etree.SchematronParseError, + isoschematron.Schematron, schema) + + def test_schematron_from_tree(self): + schema = self.parse('''\ +<sch:schema xmlns:sch="http://purl.oclc.org/dsdl/schematron"> + <sch:pattern id="number_of_entries"> + <sch:title>mandatory number_of_entries tests</sch:title> + <sch:rule context="number_of_entries"> + <sch:assert test="text()=count(../entries/entry)">[ERROR] number_of_entries (<sch:value-of select="."/>) must equal the number of entries/entry elements (<sch:value-of select="count(../entries/entry)"/>)</sch:assert> + </sch:rule> + </sch:pattern> +</sch:schema> +''') + schematron = isoschematron.Schematron(schema) + self.assert_(isinstance(schematron, isoschematron.Schematron)) + + def test_schematron_from_element(self): + schema = self.parse('''\ +<sch:schema xmlns:sch="http://purl.oclc.org/dsdl/schematron"> + <sch:pattern id="number_of_entries"> + <sch:title>mandatory number_of_entries tests</sch:title> + <sch:rule context="number_of_entries"> + <sch:assert test="text()=count(../entries/entry)">[ERROR] number_of_entries (<sch:value-of select="."/>) must equal the number of entries/entry elements (<sch:value-of select="count(../entries/entry)"/>)</sch:assert> + </sch:rule> + </sch:pattern> +</sch:schema> +''') + schematron = isoschematron.Schematron(schema.getroot()) + self.assert_(isinstance(schematron, isoschematron.Schematron)) + + def test_schematron_from_file(self): + schematron = isoschematron.Schematron(file=fileInTestDir('test.sch')) + self.assert_(isinstance(schematron, isoschematron.Schematron)) + + def test_schematron_call(self): + schema = self.parse('''\ +<sch:schema xmlns:sch="http://purl.oclc.org/dsdl/schematron"> + <sch:pattern id="number_of_entries"> + <sch:title>mandatory number_of_entries tests</sch:title> + <sch:rule context="number_of_entries"> + <sch:assert test="text()=count(../entries/entry)">[ERROR] number_of_entries (<sch:value-of select="."/>) must equal the number of entries/entry elements (<sch:value-of select="count(../entries/entry)"/>)</sch:assert> + </sch:rule> + </sch:pattern> +</sch:schema> +''') + tree_valid = self.parse('''\ +<message> + <number_of_entries>0</number_of_entries> + <entries> + </entries> +</message> +''') + tree_invalid = self.parse('''\ +<message> + <number_of_entries>3</number_of_entries> + <entries> + <entry>Entry 1</entry> + <entry>Entry 2</entry> + </entries> +</message> +''') + schematron = isoschematron.Schematron(schema) + self.assert_(schematron(tree_valid), schematron.error_log) + valid = schematron(tree_invalid) + self.assert_(not valid) + + def test_schematron_validate(self): + schema = self.parse('''\ +<sch:schema xmlns:sch="http://purl.oclc.org/dsdl/schematron"> + <sch:pattern id="number_of_entries"> + <sch:title>mandatory number_of_entries tests</sch:title> + <sch:rule context="number_of_entries"> + <sch:assert test="text()=count(../entries/entry)">[ERROR] number_of_entries (<sch:value-of select="."/>) must equal the number of entries/entry elements (<sch:value-of select="count(../entries/entry)"/>)</sch:assert> + </sch:rule> + </sch:pattern> +</sch:schema> +''') + tree_valid = self.parse('''\ +<message> + <number_of_entries>0</number_of_entries> + <entries> + </entries> +</message> +''') + tree_invalid = self.parse('''\ +<message> + <number_of_entries>3</number_of_entries> + <entries> + <entry>Entry 1</entry> + <entry>Entry 2</entry> + </entries> +</message> +''') + schematron = isoschematron.Schematron(schema) + self.assert_(schematron.validate(tree_valid), schematron.error_log) + valid = schematron.validate(tree_invalid) + self.assert_(not valid) + + def test_schematron_assertValid(self): + schema = self.parse('''\ +<sch:schema xmlns:sch="http://purl.oclc.org/dsdl/schematron"> + <sch:pattern id="number_of_entries"> + <sch:title>mandatory number_of_entries tests</sch:title> + <sch:rule context="number_of_entries"> + <sch:assert test="text()=count(../entries/entry)">[ERROR] number_of_entries (<sch:value-of select="."/>) must equal the number of entries/entry elements (<sch:value-of select="count(../entries/entry)"/>)</sch:assert> + </sch:rule> + </sch:pattern> +</sch:schema> +''') + tree_valid = self.parse('''\ +<message> + <number_of_entries>0</number_of_entries> + <entries> + </entries> +</message> +''') + tree_invalid = self.parse('''\ +<message> + <number_of_entries>3</number_of_entries> + <entries> + <entry>Entry 1</entry> + <entry>Entry 2</entry> + </entries> +</message> +''') + schematron = isoschematron.Schematron(schema) + self.assert_(schematron(tree_valid), schematron.error_log) + self.assertRaises(etree.DocumentInvalid, schematron.assertValid, + tree_invalid) + + def test_schematron_error_log(self): + schema = self.parse('''\ +<sch:schema xmlns:sch="http://purl.oclc.org/dsdl/schematron"> + <sch:pattern id="number_of_entries"> + <sch:title>mandatory number_of_entries tests</sch:title> + <sch:rule context="number_of_entries"> + <sch:assert test="text()=count(../entries/entry)">[ERROR] number_of_entries (<sch:value-of select="."/>) must equal the number of entries/entry elements (<sch:value-of select="count(../entries/entry)"/>)</sch:assert> + </sch:rule> + </sch:pattern> +</sch:schema> +''') + tree_valid = self.parse('''\ +<message> + <number_of_entries>0</number_of_entries> + <entries> + </entries> +</message> +''') + tree_invalid = self.parse('''\ +<message> + <number_of_entries>3</number_of_entries> + <entries> + <entry>Entry 1</entry> + <entry>Entry 2</entry> + </entries> +</message> +''') + schematron = isoschematron.Schematron(schema) + self.assert_(schematron(tree_valid), schematron.error_log) + valid = schematron(tree_invalid) + self.assert_(not valid) + self.assertEquals(len(schematron.error_log), 1, + 'expected single error: %s (%s errors)' % + (schematron.error_log, len(schematron.error_log))) + + def test_schematron_result_report(self): + schema = self.parse('''\ +<sch:schema xmlns:sch="http://purl.oclc.org/dsdl/schematron"> + <sch:pattern id="number_of_entries"> + <sch:title>mandatory number_of_entries tests</sch:title> + <sch:rule context="number_of_entries"> + <sch:assert test="text()=count(../entries/entry)">[ERROR] number_of_entries (<sch:value-of select="."/>) must equal the number of entries/entry elements (<sch:value-of select="count(../entries/entry)"/>)</sch:assert> + </sch:rule> + </sch:pattern> +</sch:schema> +''') + tree_valid = self.parse('''\ +<message> + <number_of_entries>0</number_of_entries> + <entries> + </entries> +</message> +''') + tree_invalid = self.parse('''\ +<message> + <number_of_entries>3</number_of_entries> + <entries> + <entry>Entry 1</entry> + <entry>Entry 2</entry> + </entries> +</message> +''') + schematron = isoschematron.Schematron(schema, store_report=True) + self.assert_(schematron(tree_valid), schematron.error_log) + valid = schematron(tree_invalid) + self.assert_(not valid) + self.assert_( + isinstance(schematron.validation_report, etree._ElementTree), + 'expected a validation report result tree, got: %s' % + (schematron.validation_report)) + + schematron = isoschematron.Schematron(schema, store_report=False) + self.assert_(schematron(tree_valid), schematron.error_log) + valid = schematron(tree_invalid) + self.assert_(not valid) + self.assert_(schematron.validation_report is None, + 'validation reporting switched off, still: %s' % + (schematron.validation_report)) + + def test_schematron_store_schematron(self): + schema = self.parse('''\ +<sch:schema xmlns:sch="http://purl.oclc.org/dsdl/schematron"> + <sch:pattern id="number_of_entries"> + <sch:title>mandatory number_of_entries tests</sch:title> + <sch:rule context="number_of_entries"> + <sch:assert test="text()=count(../entries/entry)">[ERROR] number_of_entries (<sch:value-of select="."/>) must equal the number of entries/entry elements (<sch:value-of select="count(../entries/entry)"/>)</sch:assert> + </sch:rule> + </sch:pattern> +</sch:schema> +''') + schematron = isoschematron.Schematron(schema) + self.assert_(schematron.validator_xslt is None) + + schematron = isoschematron.Schematron(schema, store_schematron=True) + self.assert_(isinstance(schematron.schematron, etree._ElementTree), + 'expected schematron schema to be stored') + + def test_schematron_store_xslt(self): + schema = self.parse('''\ +<sch:schema xmlns:sch="http://purl.oclc.org/dsdl/schematron"> + <sch:pattern id="number_of_entries"> + <sch:title>mandatory number_of_entries tests</sch:title> + <sch:rule context="number_of_entries"> + <sch:assert test="text()=count(../entries/entry)">[ERROR] number_of_entries (<sch:value-of select="."/>) must equal the number of entries/entry elements (<sch:value-of select="count(../entries/entry)"/>)</sch:assert> + </sch:rule> + </sch:pattern> +</sch:schema> +''') + schematron = isoschematron.Schematron(schema) + self.assert_(schematron.validator_xslt is None) + + schematron = isoschematron.Schematron(schema, store_xslt=True) + self.assert_(isinstance(schematron.validator_xslt, etree._ElementTree), + 'expected validator xslt to be stored') + + def test_schematron_abstract(self): + schema = self.parse('''\ +<sch:schema xmlns:sch="http://purl.oclc.org/dsdl/schematron"> + <sch:title>iso schematron validation</sch:title> + <sch:ns uri="http://www.w3.org/2001/XMLSchema-instance" prefix="xsi"/> + <sch:ns uri="http://codespeak.net/lxml/objectify/pytype" prefix="py"/> + + <!-- of course, these only really make sense when combined with a schema that + ensures datatype xs:dateTime --> + + <sch:pattern abstract="true" id="abstract.dateTime.tz_utc"> + <sch:rule context="$datetime"> + <sch:let name="tz" value="concat(substring-after(substring-after(./text(), 'T'), '+'), substring-after(substring-after(./text(), 'T'), '-'))"/> + <sch:let name="lastchar" value="substring(./text(), string-length(./text()))"/> + <sch:assert test="$lastchar='Z' or $tz='00:00'">[ERROR] element (<sch:value-of select="name(.)"/>) dateTime value (<sch:value-of select="."/>) is not qualified as UTC (tz: <sch:value-of select="$tz"/>)</sch:assert> + </sch:rule> + </sch:pattern> + + <sch:pattern abstract="true" id="abstract.dateTime.tz_utc_nillable"> + <sch:rule context="$datetime"> + <sch:let name="tz" value="concat(substring-after(substring-after(./text(), 'T'), '+'), substring-after(substring-after(./text(), 'T'), '-'))"/> + <sch:let name="lastchar" value="substring(./text(), string-length(./text()))"/> + <sch:assert test="@xsi:nil='true' or ($lastchar='Z' or $tz='00:00')">[ERROR] element (<sch:value-of select="name(.)"/>) dateTime value (<sch:value-of select="."/>) is not qualified as UTC (tz: <sch:value-of select="$tz"/>)</sch:assert> + </sch:rule> + </sch:pattern> + + <sch:pattern is-a="abstract.dateTime.tz_utc" id="datetime" > + <sch:param name="datetime" value="datetime"/> + </sch:pattern> + + <sch:pattern is-a="abstract.dateTime.tz_utc_nillable" id="nillableDatetime"> + <sch:param name="datetime" value="nillableDatetime"/> + </sch:pattern> + +</sch:schema> +''') + valid_trees = [ + self.parse('''\ +<root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> + <datetime>2009-12-10T15:21:00Z</datetime> + <nillableDatetime xsi:nil="true"/> +</root> +'''), + self.parse('''\ +<root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> + <datetime>2009-12-10T15:21:00Z</datetime> + <nillableDatetime>2009-12-10T15:21:00Z</nillableDatetime> +</root> +'''), + self.parse('''\ +<root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> + <datetime>2009-12-10T15:21:00+00:00</datetime> + <nillableDatetime>2009-12-10T15:21:00-00:00</nillableDatetime> +</root> +'''), + ] + + schematron = isoschematron.Schematron(schema) + for tree_valid in valid_trees: + self.assert_(schematron(tree_valid), schematron.error_log) + + tree_invalid = self.parse('''\ +<root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> + <datetime>2009-12-10T16:21:00+01:00</datetime> + <nillableDatetime>2009-12-10T16:21:00+01:00</nillableDatetime> +</root> +''') + expected = 2 + valid = schematron(tree_invalid) + self.assert_(not valid) + self.assertEquals( + len(schematron.error_log), expected, + 'expected %s errors: %s (%s errors)' % + (expected, schematron.error_log, len(schematron.error_log))) + + tree_invalid = self.parse('''\ +<root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> + <datetime xsi:nil="true"/> + <nillableDatetime>2009-12-10T16:21:00Z</nillableDatetime> +</root> +''') + expected = 1 + valid = schematron(tree_invalid) + self.assert_(not valid) + self.assertEquals( + len(schematron.error_log), expected, + 'expected %s errors: %s (%s errors)' % + (expected, schematron.error_log, len(schematron.error_log))) + + def test_schematron_phases(self): + schema = self.parse('''\ +<sch:schema xmlns:sch="http://purl.oclc.org/dsdl/schematron"> + <sch:title>iso schematron validation</sch:title> + <sch:ns uri="http://www.w3.org/2001/XMLSchema-instance" prefix="xsi"/> + <sch:ns uri="http://codespeak.net/lxml/objectify/pytype" prefix="py"/> + + <sch:phase id="mandatory"> + <sch:active pattern="number_of_entries"/> + </sch:phase> + + <sch:phase id="datetime_checks"> + <sch:active pattern="datetime"/> + <sch:active pattern="nillableDatetime"/> + </sch:phase> + + <sch:phase id="full"> + <sch:active pattern="number_of_entries"/> + <sch:active pattern="datetime"/> + <sch:active pattern="nillableDatetime"/> + </sch:phase> + + <!-- of course, these only really make sense when combined with a schema that + ensures datatype xs:dateTime --> + + <sch:pattern abstract="true" id="abstract.dateTime.tz_utc"> + <sch:rule context="$datetime"> + <sch:let name="tz" value="concat(substring-after(substring-after(./text(), 'T'), '+'), substring-after(substring-after(./text(), 'T'), '-'))"/> + <sch:let name="lastchar" value="substring(./text(), string-length(./text()))"/> + <sch:assert test="$lastchar='Z' or $tz='00:00'">[ERROR] element (<sch:value-of select="name(.)"/>) dateTime value (<sch:value-of select="."/>) is not qualified as UTC (tz: <sch:value-of select="$tz"/>)</sch:assert> + </sch:rule> + </sch:pattern> + + <sch:pattern abstract="true" id="abstract.dateTime.tz_utc_nillable"> + <sch:rule context="$datetime"> + <sch:let name="tz" value="concat(substring-after(substring-after(./text(), 'T'), '+'), substring-after(substring-after(./text(), 'T'), '-'))"/> + <sch:let name="lastchar" value="substring(./text(), string-length(./text()))"/> + <sch:assert test="@xsi:nil='true' or ($lastchar='Z' or $tz='00:00')">[ERROR] element (<sch:value-of select="name(.)"/>) dateTime value (<sch:value-of select="."/>) is not qualified as UTC (tz: <sch:value-of select="$tz"/>)</sch:assert> + </sch:rule> + </sch:pattern> + + <sch:pattern id="number_of_entries"> + <sch:title>mandatory number_of_entries test</sch:title> + <sch:rule context="number_of_entries"> + <sch:assert test="text()=count(../entries/entry)">[ERROR] number_of_entries (<sch:value-of select="."/>) must equal the number of entries/entry elements (<sch:value-of select="count(../entries/entry)"/>)</sch:assert> + </sch:rule> + </sch:pattern> + + <sch:pattern id="datetime" is-a="abstract.dateTime.tz_utc"> + <sch:param name="datetime" value="datetime"/> + </sch:pattern> + + <sch:pattern id="nillableDatetime" is-a="abstract.dateTime.tz_utc_nillable"> + <sch:param name="datetime" value="nillableDatetime"/> + </sch:pattern> + +</sch:schema> +''') + tree_valid = self.parse('''\ +<message xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> + <datetime>2009-12-10T15:21:00Z</datetime> + <nillableDatetime xsi:nil="true"/> + <number_of_entries>0</number_of_entries> + <entries> + </entries> +</message> +''') + tree_invalid = self.parse('''\ +<message> + <datetime>2009-12-10T16:21:00+01:00</datetime> + <nillableDatetime>2009-12-10T16:21:00+01:00</nillableDatetime> + <number_of_entries>3</number_of_entries> + <entries> + <entry>Entry 1</entry> + <entry>Entry 2</entry> + </entries> +</message> +''') + # check everything (default phase #ALL) + schematron = isoschematron.Schematron(schema) + self.assert_(schematron(tree_valid), schematron.error_log) + expected = 3 + valid = schematron(tree_invalid) + self.assert_(not valid) + self.assertEquals( + len(schematron.error_log), expected, + 'expected %s errors: %s (%s errors)' % + (expected, schematron.error_log, len(schematron.error_log))) + + # check phase mandatory + schematron = isoschematron.Schematron( + schema, compile_params={'phase': 'mandatory'}) + self.assert_(schematron(tree_valid), schematron.error_log) + expected = 1 + valid = schematron(tree_invalid) + self.assert_(not valid) + self.assertEquals( + len(schematron.error_log), expected, + 'expected %s errors: %s (%s errors)' % + (expected, schematron.error_log, len(schematron.error_log))) + + # check phase datetime_checks + schematron = isoschematron.Schematron( + schema, compile_params={'phase': 'datetime_checks'}) + self.assert_(schematron(tree_valid), schematron.error_log) + expected = 2 + valid = schematron(tree_invalid) + self.assert_(not valid) + self.assertEquals( + len(schematron.error_log), expected, + 'expected %s errors: %s (%s errors)' % + (expected, schematron.error_log, len(schematron.error_log))) + + # check phase full + schematron = isoschematron.Schematron( + schema, compile_params={'phase': 'full'}) + self.assert_(schematron(tree_valid), schematron.error_log) + expected = 3 + valid = schematron(tree_invalid) + self.assert_(not valid) + self.assertEquals( + len(schematron.error_log), expected, + 'expected %s errors: %s (%s errors)' % + (expected, schematron.error_log, len(schematron.error_log))) + + def test_schematron_phases_kwarg(self): + schema = self.parse('''\ +<sch:schema xmlns:sch="http://purl.oclc.org/dsdl/schematron"> + <sch:title>iso schematron validation</sch:title> + <sch:ns uri="http://www.w3.org/2001/XMLSchema-instance" prefix="xsi"/> + <sch:ns uri="http://codespeak.net/lxml/objectify/pytype" prefix="py"/> + + <sch:phase id="mandatory"> + <sch:active pattern="number_of_entries"/> + </sch:phase> + + <sch:phase id="datetime_checks"> + <sch:active pattern="datetime"/> + <sch:active pattern="nillableDatetime"/> + </sch:phase> + + <sch:phase id="full"> + <sch:active pattern="number_of_entries"/> + <sch:active pattern="datetime"/> + <sch:active pattern="nillableDatetime"/> + </sch:phase> + + <!-- of course, these only really make sense when combined with a schema that + ensures datatype xs:dateTime --> + + <sch:pattern abstract="true" id="abstract.dateTime.tz_utc"> + <sch:rule context="$datetime"> + <sch:let name="tz" value="concat(substring-after(substring-after(./text(), 'T'), '+'), substring-after(substring-after(./text(), 'T'), '-'))"/> + <sch:let name="lastchar" value="substring(./text(), string-length(./text()))"/> + <sch:assert test="$lastchar='Z' or $tz='00:00'">[ERROR] element (<sch:value-of select="name(.)"/>) dateTime value (<sch:value-of select="."/>) is not qualified as UTC (tz: <sch:value-of select="$tz"/>)</sch:assert> + </sch:rule> + </sch:pattern> + + <sch:pattern abstract="true" id="abstract.dateTime.tz_utc_nillable"> + <sch:rule context="$datetime"> + <sch:let name="tz" value="concat(substring-after(substring-after(./text(), 'T'), '+'), substring-after(substring-after(./text(), 'T'), '-'))"/> + <sch:let name="lastchar" value="substring(./text(), string-length(./text()))"/> + <sch:assert test="@xsi:nil='true' or ($lastchar='Z' or $tz='00:00')">[ERROR] element (<sch:value-of select="name(.)"/>) dateTime value (<sch:value-of select="."/>) is not qualified as UTC (tz: <sch:value-of select="$tz"/>)</sch:assert> + </sch:rule> + </sch:pattern> + + <sch:pattern id="number_of_entries"> + <sch:title>mandatory number_of_entries test</sch:title> + <sch:rule context="number_of_entries"> + <sch:assert test="text()=count(../entries/entry)">[ERROR] number_of_entries (<sch:value-of select="."/>) must equal the number of entries/entry elements (<sch:value-of select="count(../entries/entry)"/>)</sch:assert> + </sch:rule> + </sch:pattern> + + <sch:pattern id="datetime" is-a="abstract.dateTime.tz_utc"> + <sch:param name="datetime" value="datetime"/> + </sch:pattern> + + <sch:pattern id="nillableDatetime" is-a="abstract.dateTime.tz_utc_nillable"> + <sch:param name="datetime" value="nillableDatetime"/> + </sch:pattern> + +</sch:schema> +''') + tree_valid = self.parse('''\ +<message xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> + <datetime>2009-12-10T15:21:00Z</datetime> + <nillableDatetime xsi:nil="true"/> + <number_of_entries>0</number_of_entries> + <entries> + </entries> +</message> +''') + tree_invalid = self.parse('''\ +<message> + <datetime>2009-12-10T16:21:00+01:00</datetime> + <nillableDatetime>2009-12-10T16:21:00+01:00</nillableDatetime> + <number_of_entries>3</number_of_entries> + <entries> + <entry>Entry 1</entry> + <entry>Entry 2</entry> + </entries> +</message> +''') + # check everything (default phase #ALL) + schematron = isoschematron.Schematron(schema) + self.assert_(schematron(tree_valid), schematron.error_log) + expected = 3 + valid = schematron(tree_invalid) + self.assert_(not valid) + self.assertEquals( + len(schematron.error_log), expected, + 'expected %s errors: %s (%s errors)' % + (expected, schematron.error_log, len(schematron.error_log))) + + # check phase mandatory + schematron = isoschematron.Schematron(schema, phase='mandatory') + self.assert_(schematron(tree_valid), schematron.error_log) + expected = 1 + valid = schematron(tree_invalid) + self.assert_(not valid) + self.assertEquals( + len(schematron.error_log), expected, + 'expected %s errors: %s (%s errors)' % + (expected, schematron.error_log, len(schematron.error_log))) + + # check phase datetime_checks + schematron = isoschematron.Schematron(schema, phase='datetime_checks') + self.assert_(schematron(tree_valid), schematron.error_log) + expected = 2 + valid = schematron(tree_invalid) + self.assert_(not valid) + self.assertEquals( + len(schematron.error_log), expected, + 'expected %s errors: %s (%s errors)' % + (expected, schematron.error_log, len(schematron.error_log))) + + # check phase full + schematron = isoschematron.Schematron(schema, phase='full') + self.assert_(schematron(tree_valid), schematron.error_log) + expected = 3 + valid = schematron(tree_invalid) + self.assert_(not valid) + self.assertEquals( + len(schematron.error_log), expected, 'expected %s errors: %s (%s errors)' % + (expected, schematron.error_log, len(schematron.error_log))) + + def test_schematron_xmlschema_embedded(self): + schema = self.parse('''\ +<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" + xmlns:sch="http://purl.oclc.org/dsdl/schematron"> + <xs:element name="message"> + <xs:complexType> + <xs:sequence> + <xs:element name="number_of_entries" type="xs:positiveInteger"> + <xs:annotation> + <xs:appinfo> + <sch:pattern id="number_of_entries"> + <sch:title>mandatory number_of_entries tests</sch:title> + <sch:rule context="number_of_entries"> + <sch:assert test="text()=count(../entries/entry)">[ERROR] number_of_entries (<sch:value-of select="."/>) must equal the number of entries/entry elements (<sch:value-of select="count(../entries/entry)"/>)</sch:assert> + </sch:rule> + </sch:pattern> + </xs:appinfo> + </xs:annotation> + </xs:element> + <xs:element name="entries"> + <xs:complexType> + <xs:sequence> + <xs:element name="entry" type="xs:string" minOccurs="0" maxOccurs="unbounded"/> + </xs:sequence> + </xs:complexType> + </xs:element> + </xs:sequence> + </xs:complexType> + </xs:element> +</xs:schema> +''') + tree_valid = self.parse('''\ +<message> + <number_of_entries>2</number_of_entries> + <entries> + <entry>Entry 1</entry> + <entry>Entry 2</entry> + </entries> +</message> +''') + tree_invalid = self.parse('''\ +<message> + <number_of_entries>1</number_of_entries> + <entries> + <entry>Entry 1</entry> + <entry>Entry 2</entry> + </entries> +</message> +''') + xmlschema = etree.XMLSchema(schema) + schematron = isoschematron.Schematron(schema) + # fwiw, this must also be XMLSchema-valid + self.assert_(xmlschema(tree_valid), xmlschema.error_log) + self.assert_(schematron(tree_valid)) + # still schema-valid + self.assert_(xmlschema(tree_invalid), xmlschema.error_log) + self.assert_(not schematron(tree_invalid)) + + def test_schematron_relaxng_embedded(self): + schema = self.parse('''\ +<grammar xmlns="http://relaxng.org/ns/structure/1.0" + xmlns:sch="http://purl.oclc.org/dsdl/schematron" + datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes"> + <start> + <ref name="message"/> + </start> + <define name="message"> + <element name="message"> + <element name="number_of_entries"> + <!-- RelaxNG can be mixed freely with stuff from other namespaces --> + <sch:pattern id="number_of_entries"> + <sch:title>mandatory number_of_entries tests</sch:title> + <sch:rule context="number_of_entries"> + <sch:assert test="text()=count(../entries/entry)">[ERROR] number_of_entries (<sch:value-of select="."/>) must equal the number of entries/entry elements (<sch:value-of select="count(../entries/entry)"/>)</sch:assert> + </sch:rule> + </sch:pattern> + <data type="positiveInteger"/> + </element> + <element name="entries"> + <zeroOrMore> + <element name="entry"><data type="string"/></element> + </zeroOrMore> + </element> + </element> + </define> +</grammar> +''') + tree_valid = self.parse('''\ +<message> + <number_of_entries>2</number_of_entries> + <entries> + <entry>Entry 1</entry> + <entry>Entry 2</entry> + </entries> +</message> +''') + tree_invalid = self.parse('''\ +<message> + <number_of_entries>1</number_of_entries> + <entries> + <entry>Entry 1</entry> + <entry>Entry 2</entry> + </entries> +</message> +''') + relaxng = etree.RelaxNG(schema) + schematron = isoschematron.Schematron(schema) + # fwiw, this must also be RelaxNG-valid + self.assert_(relaxng(tree_valid), relaxng.error_log) + self.assert_(schematron(tree_valid)) + # still schema-valid + self.assert_(relaxng(tree_invalid), relaxng.error_log) + self.assert_(not schematron(tree_invalid)) + + #TODO: test xslt parameters for inclusion, expand & compile steps (?) + + +def test_suite(): + suite = unittest.TestSuite() + suite.addTests([unittest.makeSuite(ETreeISOSchematronTestCase)]) + suite.addTests( + [make_doctest('../../../doc/validation.txt')]) + return suite + +if __name__ == '__main__': + print('to test use test.py %s' % __file__) |